使用Jackson中的Custom Serializer对集合进行多态序列化

时间:2013-01-22 08:04:24

标签: jackson resteasy

我如何告诉jackson为每种Generic集合类型使用不同的序列化程序。 我有一个CustomProductsSerializer和一个CustomClientsSerializer

    public class ProductsListSerializer extends JsonSerializer<List<Products>>{

    // logic to insert __metadata , __count into json
    public void serialize(List<Products> value, JsonGenerator jgen, SerializerProvider provider) {
     .... .... .... 
     }
    // always returns ArrayList.class 
    public Class<List<Instrument>> handledType() {
    Class c = var.getClass();
    return c;
    }
    }


    public class CustomClientsSerializer extends JsonSerializer<List<Clients>>{

    // logic to insert __metadata , __count into json
    public void serialize(List<Clients> value, JsonGenerator jgen, SerializerProvider provider){
     .... .... .... 
     }
     .... .... .... 
    // always returns ArrayList.class 
    public Class<List<Clients>> handledType() {
    Class c = var.getClass();
    return c;
    }
    }

我已使用SimpleModule注册了两个序列化程序。 问题是,由于两种情况下的processedType都返回ArrayList.class,因此下面的客户端序列化失败并带有类强制转换异常。

    List<Clients> clients;
    // code to get clients from database. 
    String jsonString = mapper.writeValueAsString(clients);

问题是如何告诉jackson使用哪个序列化器?

我正在使用杰克逊作为json序列化器,并且重新安装。

- 编辑以响应@ HiJon89 我有一个返回'List'的业务对象和另一个返回List的业务对象。我需要的方式是当Busines对象返回一个'List'时,ProductsListSerializer应该用于整个List。当Business Object返回'List时,CustomClientsSerializer应该用于整个List。在我的用例中,我将附加元素附加到序列化的json'例如:__ count,__ metadata,__references'每个Collection只有一个__count。 Contentusing只能用于属性。有什么建议 ?

2 个答案:

答案 0 :(得分:3)

您是否真的希望此类型的序列化/反序列化在集合内部时以不同方式处理?如果没有,您可以使用@JsonSerialize(using = ProductSerializer.class)之类的内容注释Product类,并且只要遇到Product(包括内部集合),Jackson就会使用您的序列化程序。

如果你真的需要在集合内部进行不同的处理,你可以用类似@JsonSerialize(using = ProductSerializer.class, contentUsing = ProductInCollectionSerializer.class)之类的东西来注释类。这将做与上面相同的事情,除了当Jackson遇到包含ProductInCollectionSerializer的集合时,Product将用于序列化。

根据评论编辑:

杰克逊会将一个集合序列化为一个JSON数组,因此没有地方可以放置这些额外的属性。听起来你好像你想让Jackson返回一个包含JSON数组的JSON对象以及一些额外的元数据。在这种情况下,最好的方法是将List<Product>包装在一个对象中,例如:

public class ProductListWrapper {
    private final List<Product> products;

    public ProductListWrapper(List<Product> products) {
        this.products = products;
    }

    public List<Product> getProducts() {
        return products;
    }

    @JsonProperty("_count")
    public int getCount() {
        return getProducts() == null ? 0 : getProducts().size();
    }
}
然后,杰克逊可以序列化这个对象而无需编写任何自定义序列化程序,并且会产生如下的JSON:

{
    "products": [....]
    "_count": 25
}

答案 1 :(得分:0)

可以通过扩展simplemodule或扩展com.fasterxml.jackson.databind.Module来实现。我采取了第二种方法。以下是变更摘要

添加了将JavaType作为参数的方法。

    public <T> TypedModule addSerializer(JavaType type, JsonSerializer<T> ser)
{
    if (_serializers == null) {
        _serializers = new TypedSerializers();
    }
    _serializers.addSerializer(type, ser);
    return this;
}

上述方法将新的序列化添加到

protected TypedSerializers _serializers = null;

类'TypedSerializers扩展Serializers.Base'将所有类型化的序列化器存储在HashMap中。

更改了TypedSerializers

中findSerializer方法的逻辑
public JsonSerializer<?> findSerializer(SerializationConfig config,
        JavaType type, BeanDescription beanDesc) {

    Class<?> cls = type.getRawClass();
    ClassKey key = new ClassKey(cls);
    JsonSerializer<?> ser = null;
    if (_javaTypeMappings != null) {
        ser = _javaTypeMappings.get(type.toString());
        if (ser != null) {
            return ser;
        }
    }
 ..... process normal serilaizers ...

注册自定义序列化程序时需要进行以下更改

    TypedModule simpleModule = new TypedModule("TypedModule", new Version(1, 0, 0, null));       
    JavaType productlist = this.getTypeFactory().constructCollectionType(List.class, Product.class);
    JavaType clientlist = this.getTypeFactory().constructCollectionType(List.class, Client.class);
    simpleModule.addSerializer(productlist,  new prodctlistserializer());
    simpleModule.addSerializer(clientlist, new clientlistserializer() );
    this.registerModule(simpleModule);

结果行为是,当'Type of Product Product'传递给ObjectMapper时,它会尝试找到合适的serilaizer。我们在_javaTypeMappings的映射中维护通用序列化器。 Objecmapper执行findSerializer。 findSerializer首先在_javaTypeMappings中检查可用的序列化程序。在这种情况下,它返回prodctlistserializer。当传递“类型客户端列表”时,它返回clientlistserializer。

我已将com.fasterxml.jackson.databind.Module下的所有源代码下载到我的项目中的一个包中,并添加了上面的更改。