25

今、私はジャクソンと仕事をしていますが、それについていくつか質問があります。

初めに。2 つのサービスがあります。1 つ目はデータ収集および送信サービスで、2 つ目はこのデータを受信し、たとえばファイルに記録します。

したがって、最初のサービスには次のようなクラス階層があります。

         +----ConcreteC
         |
Base ----+----ConcreteA
         |
         +----ConcreteB

2 番目のサービスには、次のようなクラス階層があります。

ConcreteAAdapter extends ConcreteA implements Adapter {}
ConcreteBAdapter extends ConcreteB implements Adapter {}
ConcreteCAdapter extends ConcreteC implements Adapter {}

最初のサービスは について何も知りませんConcreteXAdapter

最初のサービスでデータを送信する方法:

Collection<Base> data = new LinkedBlockingQueue<Base>()
JacksonUtils utils = new JacksonUtils();
data.add(new ConcreteA());
data.add(new ConcreteB());
data.add(new ConcreteC());
...
send(utils.marshall(data));
...

public class JacksonUtils {

    public byte[] marshall(Collection<Base> data) throws IOException {
        ByteArrayOutputStream out = new ByteArrayOutputStream() {
            @Override
            public byte[] toByteArray() {
                return buf;
            }
        };

        getObjectMapper().writeValue(out, data);
        return out.toByteArray();
    }
    protected ObjectMapper getObjectMapper() {
        return new ObjectMapper();
    }

    public Object unmarshall(byte[] json) throws IOException {
        return getObjectMapper().readValue(json, Object.class);
    }

    public <T> T unmarshall(InputStream source, TypeReference<T> typeReference) throws IOException {
        return getObjectMapper().readValue(source, typeReference);
    }

    public <T> T unmarshall(byte[] json, TypeReference<T> typeReference) throws IOException {
        return getObjectMapper().readValue(json, typeReference);
    }
}

したがって、json をCollection of ( )ConcreteXAdapterではなく、 Collection of にデシリアライズしたいと考えています。私が説明した場合、私は取得したい:ConcreteXConcreteA -> ConcreteAAdapter, ConcreteB -> ConcreteBAdapter, ConcreteC -> ConcreteCAdapter

Collection [ConcreteAAdapter, ConcreteBAdapter, ConcreteCAdapter]

これどうやってするの?

4

3 に答える 3

32

この目的のために、JSONで追加情報を渡す必要があります。

@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, 
      include=JsonTypeInfo.As.PROPERTY, property="@type")
class Base {
...
}

次に、シリアル化時に@typeフィールドを追加します。

objectMapper.registerSubtypes(
            new NamedType(ConcreteAAdapter.class, "ConcreteA"),
            new NamedType(ConcreteBAdapter.class, "ConcreteB"),
            new NamedType(ConcreteCAdapter.class, "ConcreteC")
            );

// note, that for lists you need to pass TypeReference explicitly
objectMapper.writerWithType(new TypeReference<List<Base>>() {})
     .writeValueAsString(someList);


    {
      "@type" : "ConcreteA",
      ...
    }

デシリアライズでは、次のようになります。

    objectMapper.registerSubtypes(
            new NamedType(ConcreteA.class, "ConcreteA"),
            new NamedType(ConcreteB.class, "ConcreteB"),
            new NamedType(ConcreteC.class, "ConcreteC")
            );
    objectMapper.readValue(....)

詳細はこちら

于 2012-04-26T15:31:00.273 に答える
20

この問題をどのように解決したか。サンプルプロジェクトのクラス図は次のとおりです。 クラス図

だから私は逆シリアル化後にConcreteAAdapterフォームを取得したいと思います。ConcreteA

私の解決策は、ClassNameIdResolver基本クラスオブジェクトをサブタイプクラスオブジェクトに逆シリアル化する機能を追加するように拡張することです(サブタイプクラスは追加の機能や追加のフィールドを追加しません)。

ObjectMapperデシリアライズ用に作成するコードは次のとおりです。

protected ObjectMapper getObjectMapperForDeserialization() {
        ObjectMapper mapper = new ObjectMapper();

        StdTypeResolverBuilder typeResolverBuilder = new ObjectMapper.DefaultTypeResolverBuilder(ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE);
        typeResolverBuilder = typeResolverBuilder.inclusion(JsonTypeInfo.As.PROPERTY);
        typeResolverBuilder.init(JsonTypeInfo.Id.CLASS, new ClassNameIdResolver(SimpleType.construct(Base.class), TypeFactory.defaultInstance()) {
            private HashMap<Class, Class> classes = new HashMap<Class, Class>() {
                {
                    put(ConcreteA.class, ConcreteAAdapter.class);
                    put(ConcreteB.class, ConcreteBAdapter.class);
                    put(ConcreteC.class, ConcreteCAdapter.class);
                }
            };

            @Override
            public String idFromValue(Object value) {
                return (classes.containsKey(value.getClass())) ? value.getClass().getName() : null;
            }

            @Override
            public JavaType typeFromId(String id) {
                try {
                    return classes.get(Class.forName(id)) == null ? super.typeFromId(id) : _typeFactory.constructSpecializedType(_baseType, classes.get(Class.forName(id)));
                } catch (ClassNotFoundException e) {
                    // todo catch the e
                }
                return super.typeFromId(id);
            }
        });
        mapper.setDefaultTyping(typeResolverBuilder);
        return mapper;
    }

そして、これがシリアル化のために作成するコードですObjectMapper

protected ObjectMapper getObjectMapperForSerialization() {
    ObjectMapper mapper = new ObjectMapper();

    StdTypeResolverBuilder typeResolverBuilder = new ObjectMapper.DefaultTypeResolverBuilder(ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE);
    typeResolverBuilder = typeResolverBuilder.inclusion(JsonTypeInfo.As.PROPERTY);
    typeResolverBuilder.init(JsonTypeInfo.Id.CLASS, new ClassNameIdResolver(SimpleType.construct(Base.class), TypeFactory.defaultInstance()));
    mapper.setDefaultTyping(typeResolverBuilder);

    return mapper;
}

テストコード:

public static void main(String[] args) throws IOException {
    JacksonUtils JacksonUtils = new JacksonUtilsImpl();

    Collection<Base> data = new LinkedBlockingQueue<Base>();
    data.add(new ConcreteA());
    data.add(new ConcreteB());
    data.add(new ConcreteC());

    String json = JacksonUtils.marshallIntoString(data);

    System.out.println(json);

    Collection<? extends Adapter> adapters = JacksonUtils.unmarshall(json, new TypeReference<ArrayList<Adapter>>() {});

    for (Adapter adapter : adapters) {
        System.out.println(adapter.getClass().getName());
    }
}

JacksonUtilsクラスの完全なコード:

public class JacksonUtilsImpl implements JacksonUtils {

    @Override
    public byte[] marshall(Collection<Base> data) throws IOException {
        ByteArrayOutputStream out = new ByteArrayOutputStream() {
            @Override
            public byte[] toByteArray() {
                return buf;
            }
        };

        getObjectMapperForSerialization().writerWithType(new TypeReference<Collection<Base>>() {}).writeValue(out, data);
        return out.toByteArray();
    }

    @Override
    public String marshallIntoString(Collection<Base> data) throws IOException {
        return getObjectMapperForSerialization().writeValueAsString(data);
    }

    protected ObjectMapper getObjectMapperForSerialization() {
        ObjectMapper mapper = new ObjectMapper();

        StdTypeResolverBuilder typeResolverBuilder = new ObjectMapper.DefaultTypeResolverBuilder(ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE);
        typeResolverBuilder = typeResolverBuilder.inclusion(JsonTypeInfo.As.PROPERTY);
        typeResolverBuilder.init(JsonTypeInfo.Id.CLASS, new ClassNameIdResolver(SimpleType.construct(Base.class), TypeFactory.defaultInstance()));
        mapper.setDefaultTyping(typeResolverBuilder);

        return mapper;
    }

    protected ObjectMapper getObjectMapperForDeserialization() {
        ObjectMapper mapper = new ObjectMapper();

        StdTypeResolverBuilder typeResolverBuilder = new ObjectMapper.DefaultTypeResolverBuilder(ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE);
        typeResolverBuilder = typeResolverBuilder.inclusion(JsonTypeInfo.As.PROPERTY);
        typeResolverBuilder.init(JsonTypeInfo.Id.CLASS, new ClassNameIdResolver(SimpleType.construct(Base.class), TypeFactory.defaultInstance()) {
            private HashMap<Class, Class> classes = new HashMap<Class, Class>() {
                {
                    put(ConcreteA.class, ConcreteAAdapter.class);
                    put(ConcreteB.class, ConcreteBAdapter.class);
                    put(ConcreteC.class, ConcreteCAdapter.class);
                }
            };

            @Override
            public String idFromValue(Object value) {
                return (classes.containsKey(value.getClass())) ? value.getClass().getName() : null;
            }

            @Override
            public JavaType typeFromId(String id) {
                try {
                    return classes.get(Class.forName(id)) == null ? super.typeFromId(id) : _typeFactory.constructSpecializedType(_baseType, classes.get(Class.forName(id)));
                } catch (ClassNotFoundException e) {
                    // todo catch the e
                }
                return super.typeFromId(id);
            }
        });
        mapper.setDefaultTyping(typeResolverBuilder);
        return mapper;
    }

    @Override
    public Object unmarshall(byte[] json) throws IOException {
        return getObjectMapperForDeserialization().readValue(json, Object.class);
    }

    @Override
    public <T> T unmarshall(InputStream source, TypeReference<T> typeReference) throws IOException {
        return getObjectMapperForDeserialization().readValue(source, typeReference);
    }

    @Override
    public <T> T unmarshall(byte[] json, TypeReference<T> typeReference) throws IOException {
        return getObjectMapperForDeserialization().readValue(json, typeReference);
    }

    @Override
    public <T> Collection<? extends T> unmarshall(String json, Class<? extends Collection<? extends T>> klass) throws IOException {
        return getObjectMapperForDeserialization().readValue(json, klass);
    }


    @Override
    public <T> Collection<? extends T> unmarshall(String json, TypeReference typeReference) throws IOException {
        return getObjectMapperForDeserialization().readValue(json, typeReference);
    }
}
于 2012-04-27T10:49:23.817 に答える
13

私は、programmerbruce のアプローチが最も明確で作業しやすいと思います (以下の例)。関連する質問への彼の回答から情報を得ました: https://stackoverflow.com/a/6339600/1148030 および関連するブログ投稿: http://programmerbruce.blogspot.fi/2011/05/deserialize-json-with -jackson-into.html

このフレンドリーな wiki ページもチェックしてください (Eugene Retunsky の回答にも記載されています): https://github.com/FasterXML/jackson-docs/wiki/JacksonPolymorphicDeserialization

別の素晴らしい wiki ページ: https://github.com/FasterXML/jackson-docs/wiki/JacksonMixInAnnotations

アイデアを提供するための短い例を次に示します。

ObjectMapper を次のように構成します。

    mapper.getDeserializationConfig().addMixInAnnotations(Base.class, BaseMixin.class);
    mapper.getSerializationConfig().addMixInAnnotations(Base.class, BaseMixin.class);

BaseMixin クラスの例 (内部クラスとして定義するのは簡単です。)

@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="type")
@JsonSubTypes({
    @JsonSubTypes.Type(value=ConcreteA.class, name="ConcreteA"),
    @JsonSubTypes.Type(value=ConcreteB.class, name="ConcreteB")
})  
private static class BaseMixin {
}

2 番目のサービスでは、次のように BaseMixin を定義できます。

@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="type")
@JsonSubTypes({
    @JsonSubTypes.Type(value=ConcreteAAdapter.class, name="ConcreteA"),
    @JsonSubTypes.Type(value=ConcreteBAdapter.class, name="ConcreteB")
})  
private static class BaseMixin {
}
于 2012-12-19T14:48:59.363 に答える