使用Jackson自定义JSON序列化/反序列化

时间:2014-06-11 20:06:00

标签: java json serialization jackson deserialization

我有一个类层次结构,如下所示:

interface MyEntity {
    ...
}

class MyEntityRef {
    String id;
    ...
}

class MyEntityImpl {
    String id;
    String name;
    ...
}

我想将MyEntityRef的实例序列化为纯字符串:

"someEntityId"

MyEntityImpl的实例作为常规对象:

{
    id: "someEntityId",
    name: "someName"
}

然后反过来说:当反序列化MyEntity时,如果json是普通字符串,我希望将其反序列化为MyEntityRef,并转换为MyEntityImpl如果它是一个普通的json对象。

在我的实际代码中,我有许多类型的实体(即多个X / XRef / XImpl三元组)。为了避免列出所有这些,我按如下方式对接口进行了注释:

@MyEntityAnnotation(ref=MyEntityRef.class, impl=MyEntityImpl.class)
interface MyEntity {
    ...
}

根据此注释信息为任何人找出解决上述序列化/反序列化的方法的额外要点。


我尝试过的事情:我能想到的一切(SimpleDeserializers / SimpleSerializersBeanDeserializerModifier / BeanSerializerModifierTypeSerializer / {{1 }})

1 个答案:

答案 0 :(得分:2)

这在技术上可以通过使用自定义反序列化器(见下文),但似乎相当复杂,有点笨重,使用反射和类型不安全的转换。使用the bi-directional references可能会获得更好的运气,但这可能需要更改模型。

以下是基于自定义反序列化程序的示例:

public class JacksonEntity {

    @Retention(RetentionPolicy.RUNTIME)
    static public @interface MyAnnotation {
        Class<?> ref();
        Class<?> impl();
    }

    @MyAnnotation(ref = MyEntityRef.class, impl = MyEntityImpl.class)
    static public interface MyEntity {
    }

    static public class MyEntityRef implements MyEntity {
        private final String id;

        public MyEntityRef(String id) {
            this.id = id;
        }

        @JsonValue
        public String getId() {
            return id;
        }

        @Override
        public String toString() {
            return "MyEntityRef{" +
                    "id='" + id + '\'' +
                    '}';
        }
    }

    static public class MyEntityImpl implements MyEntity {
        public final String id;
        public final String name;

        @JsonCreator
        public MyEntityImpl(@JsonProperty("id") String id, @JsonProperty("name") String name) {
            this.id = id;
            this.name = name;
        }

        @Override
        public String toString() {
            return "MyEntityImpl{" +
                    "id='" + id + '\'' +
                    ", name='" + name + '\'' +
                    '}';
        }
    }

    static public class MyDeserializer extends JsonDeserializer<Object> {
        private final Class<?> refType;
        private final Class<?> implType;

        public MyDeserializer(MyAnnotation annotation) {
            this.refType = annotation.ref();
            this.implType = annotation.impl();
        }

        @Override
        public Object deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
            JsonToken jsonToken = jp.getCurrentToken();
            if (jsonToken == JsonToken.START_OBJECT) {
                return jp.readValueAs(implType);
            } else if (jsonToken == JsonToken.VALUE_STRING) {
                try {
                    return refType.getConstructor(String.class).newInstance(jp.getValueAsString());
                } catch (Exception e) {
                    throw new UnsupportedOperationException();
                }
            }
            return null;
        }
    }

    public static void main(String[] args) throws IOException {
        ObjectMapper mapper = new ObjectMapper();
        SimpleModule module = new SimpleModule();
        module.setDeserializerModifier(new BeanDeserializerModifier() {
            @Override
            public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config,
                                                          BeanDescription beanDesc,
                                                          JsonDeserializer<?> deserializer) {
                MyAnnotation myAnnotation = beanDesc.getClassAnnotations().get(MyAnnotation.class);
                // it must be interface, otherwise getting meeting recursion
                if (myAnnotation != null && beanDesc.getBeanClass().isInterface()) {
                    return new MyDeserializer(myAnnotation);
                }
                return super.modifyDeserializer(config, beanDesc, deserializer);
            }
        });
        mapper.registerModule(module);

        MyEntityRef ref = new MyEntityRef("id1");
        MyEntityImpl impl = new MyEntityImpl("id2", "value");

        String jsonRef = mapper.writeValueAsString(ref);
        System.out.println(jsonRef);
        String jsonImpl = mapper.writeValueAsString(impl);
        System.out.println(jsonImpl);

        System.out.println(mapper.readValue(jsonRef, MyEntity.class));
        System.out.println(mapper.readValue(jsonImpl, MyEntity.class));
    }
}

输出:

"id1"
{"id":"id2","name":"value"}
MyEntityRef{id='id1'}
MyEntityImpl{id='id2', name='value'}