1

我使用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"or连接"_ids",具体取决于它是被引用对象还是被引用对象的集合
  2. 只有引用对象的 id被序列化
  3. 在引用对象的集合的情况下,它们的 id 数组被序列化

关于属性名称(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注释了。

谢谢。

4

1 回答 1

1

我不确定您是否真的需要定义自定义序列化程序:值序列化程序不能正常工作吗?Jackson 确实可以自动组合这些(并涵盖匹配的数组序列化程序)。因此,对于集合案例,return this;应该适用于上下文案例。

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

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

  • 使用类型信息修改名称的自定义JacksonAnnotationIntrospector- 用于序列化或反序列化,或两者(进行单独调用)
  • 通过BeanSerializerModifier,在回调之一上类似地重命名属性。
于 2013-08-30T16:07:52.733 回答