如何使Jackson序列化器更干?

时间:2013-08-30 09:02:02

标签: java serialization ember.js jackson

我正在使用ember.js作为客户端框架。开箱即用,这个框架需要一种特定格式的JSON。我正试图让杰克逊输出那种格式。这对于回答这个问题并不重要,但提到它并对其进行了标记,因为它可能会帮助处于相同情况的更多用户。

基本上,我希望将每个引用的对象(不是根对象)作为其id输出。我会给你快速举例。 这些课程:

public abstract class BaseEntity{
    protected Long id;
}

public class Resource{
    private String name;
    private AnotherResource subResource;
    private List<AnotherResource> subResources;

    //getters and setters
}

public class SubResource{
    private String value;

    //getters and setters
}

使用这些示例实例:

// Sub resources
SubResource sr1 = new SubResource();
sr1.setId(2);
sr1.setValue("some string");
SubResource sr2 = new SubResource();
sr2.setId(3);
sr2.setValue("some string");
SubResource sr3 = new SubResource();
sr3.setId(4);
sr3.setValue("some string");
// resource
Resource r = new Resource();
r.setId(1);
r.setName("bla");
r.setSubResource(sr1);
ArrayList<SubResource> list = new ArrayList<SubResource>();
list.add(sr1);
list.add(sr2);
list.add(sr3);
r.setSubResources(list);

序列化r应输出:

{
    "resource":{
        "id": 1,
        "name": "bla",
        "sub_resource_id": 2,
        "sub_resource_ids": [
            1,
            2,
            3
        ]
    }
}

我们可以在这里注意到一些事情

  1. 键名与"_id""_ids"连接,具体取决于它是引用的对象还是引用对象的集合
  2. 引用对象的ID 被序列化
  3. 如果引用对象集合,则会序列化其数组
  4. 关于属性名称(1),我已经用@JsonProperty注释对其进行了排序。

    至于其余部分,我写了以下序列化器:

    public class BaseEntityIdSerializer extends JsonSerializer<BaseEntity> implements ContextualSerializer {
    
        public void serialize(BaseEntity value, JsonGenerator jgen,
                SerializerProvider provider) throws IOException,
                JsonProcessingException {
        }
    
        @Override
        public void serializeWithType(BaseEntity value, JsonGenerator jgen,
                SerializerProvider provider, TypeSerializer typeSer)
                throws IOException, JsonProcessingException {
            serialize(value, jgen, provider);
        }
    
        @Override
        public JsonSerializer<?> createContextual(SerializerProvider prov,
                BeanProperty property) throws JsonMappingException {
            if(property.getType().isCollectionLikeType()){
                return new BaseEntityIdCollectionSerializer();
            } else {
                return new BaseEntityIdSimpleSerializer();
            }
        }
    
        public class BaseEntityIdSimpleSerializer extends StdSerializer<BaseEntity>{
    
            public BaseEntityIdSimpleSerializer(){
                super(BaseEntity.class);
            }
    
            @Override
            public void serialize(BaseEntity value, JsonGenerator jgen,
                    SerializerProvider provider) throws IOException,
                    JsonGenerationException {
                jgen.writeNumber(value.getId());
            }
    
            @Override
            public void serializeWithType(BaseEntity value, JsonGenerator jgen,
                    SerializerProvider provider, TypeSerializer typeSer)
                    throws IOException, JsonProcessingException {
                serialize(value, jgen, provider);
            }
        }
    
        public class BaseEntityIdCollectionSerializer extends StdSerializer<Collection<? extends BaseEntity>>{
    
            public BaseEntityIdCollectionSerializer(){
                super(Collection.class, false);
            }
    
            @Override
            public void serialize(Collection<? extends BaseEntity> value,
                    JsonGenerator jgen, SerializerProvider provider)
                    throws IOException, JsonGenerationException {
                jgen.writeStartArray();
                for(BaseEntity b:value){
                    jgen.writeNumber(b.getId());
                }
                jgen.writeEndArray();
            }
    
            @Override
            public void serializeWithType(Collection<? extends BaseEntity> value, JsonGenerator jgen,
                    SerializerProvider provider, TypeSerializer typeSer)
                    throws IOException, JsonProcessingException {
                serialize(value, jgen, provider);
            }
        }
    }
    

    然后使用@JsonSerialize(using=BaseEntityIdSerializer.class)

    这可以胜任。输出正确的JSON。但是我觉得我在重复很多代码。例如,我正在为集合和单个对象编写不同的序列化程序类。我希望多次使用单个序列化器。更可组合的东西。 显然我正在使用错误的类来解析序列化器(我被迫实现serialize但我什么也没做。)

    您的见解是什么?如何改进此序列化程序? 此外,是否可以处理序列化程序中的属性名称(连接“_id”)?这样我可以不使用@JsonProperty注释。

    感谢。

1 个答案:

答案 0 :(得分:1)

我不确定你真的需要定义自定义序列化程序:不值得序列化程序工作得很好吗?杰克逊确实可以自动组合这些(并覆盖匹配的阵列序列化器)。因此,对于集合案例,return this;应该适用于上下文案例。

属性重命名在值序列化程序级别不起作用,因为这样做取决于POJO序列化程序(BeanSerializer)。也就是说,值序列化程序不会写入属性名称(如果需要,它已经被JSON对象调用;或者对于JSON数组,根级别值被省略)。 这是设计的一部分,其中Jackson 1.0的不同结构可能有意义(使值序列化器有2个方法;一个用于数组元素的“简单”值,根级别;第二个用于“命名”属性值),但它是来不及改变它。

但是:您可以通过以下方式处理序列化程序之外的重命名:

  • 使用类型信息修改名称的自定义JacksonAnnotationIntrospector - 用于序列化或反序列化,或两者兼而有之(单独调用)
  • 通过BeanSerializerModifier,在其中一个回调上重命名属性。