Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generic Map<String, Object> JSON marshalling and unmarshalling with Moxy

Is there any way to marshal Map<String, Object> with Moxy to JSON so that the result uses natural constructs of JSON?
That is, the keys are strings and for all possible values following rules apply (possibly not a complete set):

  • Number (e.g., Integer) becomes a JSON number (or a string if it's too big)
  • String becomes a JSON string
  • Set, Array, Iterable become a JSON array
  • and finally for a Map<String, Object>, the same rules are recursively applied
  • any other object is marshalled in a natural Moxy way

There is already an example how to marshal Map<String, Integer> and other specific maps by using @XmlVariableNode (see http://blog.bdoughan.com/2013/06/moxys-xmlvariablenode-using-maps-key-as.html?m=1), but I was unable to extend this idea to a point where also subtypes can be inserted as a value.

Note that Moxy should be able to unmarshal the JSON back to the original Map.

Jackson is capable of doing this by default.

like image 599
Stepan Vavra Avatar asked Nov 22 '25 01:11

Stepan Vavra


1 Answers

I've tried to get something like you want with JAXB RI (XML):

@XmlRootElement
public class Foo {
    @XmlJavaTypeAdapter(MapAdapter.class)
    public Map<String, Object> map;
}

public class MapAdapter extends XmlAdapter<MapEntry[], Map<String, Object>> {
    @Override
    public Map<String, Object> unmarshal(MapEntry[] v) throws Exception {
        Map<String, Object> map = new HashMap<>();
        for (MapEntry me : v)
            map.put(me.key, me.value);
        return map;
    }
    @Override
    public MapEntry[] marshal(Map<String, Object> v) throws Exception {
        MapEntry[] mes = new MapEntry[v.size()];
        int i = 0;
        for (Map.Entry<String, Object> entry : v.entrySet())
            mes[i++] = new MapEntry(entry.getKey(), entry.getValue());
        return mes;
    }
}

public class MapEntry {
    public String key;
    public Object value;

    public MapEntry() {}

    public MapEntry(String key, Object value) {
        this.key = key;
        this.value = value;
    }
}

Unfortunately MOXy has some bug ( https://bugs.eclipse.org/bugs/show_bug.cgi?id=465014 ) and is not able to handle this.

If you need MOXy then you have to use the approach described in Blaise's blog:

@XmlRootElement
public class MoxyFoo {
    @XmlJavaTypeAdapter(MoxyMapAdapter.class)
    public Map<String, Object> map = new HashMap<>();
}

public class MoxyMapAdapter extends XmlAdapter<MoxyMapAdapter.AdaptedMap, Map<String, Object>> {
    @Override
    public AdaptedMap marshal(Map<String, Object> map) throws Exception {
        AdaptedMap adaptedMap = new AdaptedMap();
        for (Entry<String, Object> entry : map.entrySet()) {
            AdaptedEntry adaptedEntry = new AdaptedEntry();
            adaptedEntry.key = entry.getKey();
            adaptedEntry.value = entry.getValue();
            adaptedMap.entries.add(adaptedEntry);
        }
        return adaptedMap;
    }
    @Override
    public Map<String, Object> unmarshal(AdaptedMap adaptedMap) throws Exception {
        List<AdaptedEntry> adaptedEntries = adaptedMap.entries;
        Map<String, Object> map = new HashMap<>(adaptedEntries.size());
        for (AdaptedEntry adaptedEntry : adaptedEntries) {
            map.put(adaptedEntry.key, adaptedEntry.value);
        }
        return map;
    }

    public static class AdaptedMap {
        @XmlVariableNode("key")
        List<AdaptedEntry> entries = new ArrayList<>();
    }

    public static class AdaptedEntry {
        @XmlTransient
        public String key;
        @XmlValue
        public Object value;
    }
}

It's working fine for XML, but not for JSON. For JSON only marshalling is working now. I've filled a bug to fix unmarshalling: https://bugs.eclipse.org/bugs/show_bug.cgi?id=465016 .

like image 178
Iaroslav Savytskyi Avatar answered Nov 24 '25 14:11

Iaroslav Savytskyi



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!