Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import com.fasterxml.jackson.annotation.JsonCreator;
- import com.fasterxml.jackson.annotation.JsonProperty;
- import com.fasterxml.jackson.annotation.JsonTypeInfo;
- import com.fasterxml.jackson.core.JsonGenerator;
- import com.fasterxml.jackson.core.JsonParser;
- import com.fasterxml.jackson.core.JsonProcessingException;
- import com.fasterxml.jackson.core.type.WritableTypeId;
- import com.fasterxml.jackson.databind.DeserializationContext;
- import com.fasterxml.jackson.databind.ObjectMapper;
- import com.fasterxml.jackson.databind.SerializerProvider;
- import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
- import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
- import com.fasterxml.jackson.databind.module.SimpleModule;
- import com.fasterxml.jackson.databind.ser.std.StdSerializer;
- import java.io.IOException;
- import java.util.ArrayList;
- import java.util.HashMap;
- import java.util.List;
- import java.util.Map;
- import static com.fasterxml.jackson.core.JsonToken.START_OBJECT;
- //for tests, simple run...
- class ProblematicClass {
- private Map<String, Object> map;
- public ProblematicClass() {
- this.map = new HashMap<>();
- }
- public Boolean isEmpty() {
- return map.isEmpty();
- }
- public Map<String, Object> getMap() {
- return map;
- }
- public ProblematicClass put(String key, String value) {
- map.put(key, value);
- return this;
- }
- public ProblematicClass put(String key, Integer value) {
- map.put(key, value);
- return this;
- }
- //put (..., boolean), put (..., Float) etc.
- public String toJson() throws JsonProcessingException {
- //e.g. some internal logic (as for JsonObject)
- //this is just example (I know that everything is written as string)
- ObjectMapper mapper = new ObjectMapper();
- return mapper.writeValueAsString(map);
- }
- public static ProblematicClass fromJson(String json) throws IOException {
- ProblematicClass instance = new ProblematicClass();
- ObjectMapper mapper = new ObjectMapper();
- instance.map = (Map<String, Object>) mapper.readValue(json, Map.class);
- return instance;
- }
- //getString, getInteger, getFloat etc.
- }
- class Wrapper<T> {
- @JsonProperty("info")
- private String someInfo;
- @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, property = "@class")
- private List<T> data;
- @JsonCreator
- public Wrapper(@JsonProperty("info") String someInfo, @JsonProperty("data") List<T> data) {
- this.someInfo = someInfo;
- this.data = data;
- }
- public static <T> Wrapper<T> fromJson(String json) {
- if (json == null) {
- return null;
- }
- ObjectMapper mapper = new ObjectMapper();
- SimpleModule module = new SimpleModule();
- //in test, I've hard coded deserializer for ProblematicClass
- module.addDeserializer(ProblematicClass.class, new ProblematicClassDeserializer());
- mapper.registerModule(module);
- try {
- return mapper.readValue(json, Wrapper.class);
- } catch (IOException e) {
- throw new IllegalArgumentException("Cannot deserialize Wrapper from Json. Message: " + e.getMessage(), e);
- }
- }
- public String toJson() {
- ObjectMapper mapper = new ObjectMapper();
- SimpleModule module = new SimpleModule();
- //in test, I've hard coded deserializer for ProblematicClass
- module.addSerializer(ProblematicClass.class, new ProblematicClassSerializer());
- mapper.registerModule(module);
- try {
- return mapper.writeValueAsString(this);
- } catch (JsonProcessingException e) {
- throw new IllegalStateException("Cannot serialize Wrapper to Json. Message: " + e.getMessage(), e);
- }
- }
- }
- class ProblematicClassSerializer extends StdSerializer<ProblematicClass> {
- public ProblematicClassSerializer() {
- this(null);
- }
- protected ProblematicClassSerializer(Class<ProblematicClass> t) {
- super(t);
- }
- @Override
- public void serializeWithType(ProblematicClass value, JsonGenerator gen, SerializerProvider serializers, TypeSerializer typeSer) throws IOException {
- WritableTypeId typeId = typeSer.typeId(value, START_OBJECT);
- typeSer.writeTypePrefix(gen, typeId);
- gen.writeFieldName("@json");
- serialize(value, gen, serializers); //our custom serialize method written below
- typeSer.writeTypeSuffix(gen, typeId);
- }
- @Override
- public void serialize(ProblematicClass value, JsonGenerator jgen, SerializerProvider serializerProvider) throws IOException {
- jgen.writeRawValue(value == null ? null : value.toJson());
- }
- }
- class ProblematicClassDeserializer extends StdDeserializer<ProblematicClass> {
- public ProblematicClassDeserializer() {
- this(null);
- }
- protected ProblematicClassDeserializer(Class<?> vc) {
- super(vc);
- }
- @Override
- public ProblematicClass deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
- String json = jsonParser.getCodec().readTree(jsonParser).toString(); //this is actual json representation
- ObjectMapper mapper = new ObjectMapper();
- Map<String, Object> map = mapper.readValue(json, Map.class); //I know this is redundant, but for example it is fine
- return json == null ? null : ProblematicClass.fromJson(mapper.writeValueAsString(map.get("@json")));
- //sometimes I got {"key": "value"} //when serialize is called (ProblematicClass wrapped)
- //and sometimes I got {"@json": {"key": "value"}} //when using bare ProblematicClass as in Wrapper<ProblematicClass>
- //I need to distinguish these two cases but I don't know when is te first and when second
- //because deserializationContext is not giving me these information
- }
- }
- class SomeRealClass {
- public ProblematicClass problematicClass = ProblematicClass.fromJson("{\"a\": \"10\"}");
- public Integer i1 = 10, i2 = 20;
- SomeRealClass() throws IOException {
- }
- }
- public class TestMain {
- public static void main(String[] args) throws IOException {
- //without (de)serializes this does not work as it's json is not deserializable (because of 'empty' key)
- // {"data":[{"@class":"com.test.ProblematicClass","map":{"a":"10"},"empty":false},
- // {"@class":"com.test.ProblematicClass","map":{"b":"20"},"empty":false}],"info":"info"}
- //when I hardcode (de)serializer then this (correct) json is produced:
- // {"info":"info","data":[{"@class":"com.test.ProblematicClass","@json":{"a":"10"}},
- // {"@class":"com.test.ProblematicClass","@json":{"b":"20"}}]}
- {
- List<ProblematicClass> list = new ArrayList<>();
- list.add(ProblematicClass.fromJson("{\"a\": \"10\"}"));
- list.add(ProblematicClass.fromJson("{\"b\": \"20\"}"));
- Wrapper<ProblematicClass> wrapper = new Wrapper<>("info", list);
- String json = wrapper.toJson();
- Wrapper<ProblematicClass> copy = Wrapper.fromJson(json);
- }
- //---------------------------------------------------------
- //but, when container class is used (SomeRealClass)
- //this @json is redundant, so I wrote it only in serializeWithType
- //resulting JSON is correct? there is no @json, like: "problematicClass": {"@json": {"a": "10}}
- //and deserialization fails (map.get("@json") is null)
- // {"info":"info-real","data":[{"@class":"com.test.SomeRealClass","problematicClass":{"a":"10"},"i1":10,"i2":20},
- // {"@class":"com.test.SomeRealClass","problematicClass":{"a":"10"},"i1":10,"i2":20}]}
- {
- List<SomeRealClass> list = new ArrayList<>();
- list.add(new SomeRealClass());
- list.add(new SomeRealClass());
- Wrapper<SomeRealClass> wrapper = new Wrapper<>("info-real", list);
- String json = wrapper.toJson();
- Wrapper<SomeRealClass> copy = Wrapper.fromJson(json);
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement