【问题标题】:Spring Framework JSON Serialization to Array rather than ObjectSpring Framework JSON 序列化为数组而不是对象
【发布时间】:2015-03-18 21:31:54
【问题描述】:

在我使用 Spring 的 Web 应用程序中,我们希望使用自定义 JSON 结构。 Spring 默认采用这样的 POJO:

public class Model {

    private int id;
    private String name;

    public Model(){}

    public Model(int id, String name){
        this.id = id;
        this.name = name;
    }
}

把它变成这样:

{"id":1, "name":"Bob"}

对于我们的应用程序,我们希望将其变成这样:

[1, "Bob"]

我想使用 Spring 的默认序列化逻辑来检测 Java 类型(int、String、Collection 等)并映射到适当的 JSON 类型,但只需将包装对象更改为数组而不是对象和字段。

这是我目前拥有的序列化器(将在模型中使用 @JsonSerialize(using = Serializer.class) 实现),但不希望重写 Spring 已经实现的所有逻辑。

public class Serializer extends JsonSerializer<Model> {
    @Override
    public void serialize(Model value, JsonGenerator jgen, SerializerProvider provider)
        throws IOException, JsonProcessingException {

        jgen.writeStartArray();
        jgen.writeString(value.id);
        .... other values ...
        jgen.writeEndArray();

    }
}

如何连接到预先存在的序列化程序,以便这个新的序列化程序可以像默认的那样与任何 POJO 一起工作(不仅仅是 Model 类,还有我们需要序列化到数组的任何类似或子类)?这可能具有混合属性,并且没有特定的属性命名约定。

我想避免为每个不同的模型类(...其他值...)部分编写自定义序列化程序。

【问题讨论】:

  • 序列化器是正确的做法。
  • 所以不是{"id":1},而是[1],而不是{"id":[1]}
  • 我认为您找不到通用的方法来执行此操作。这是一个非常具体的要求(我建议反对)。没有杰克逊注释会给你这种行为。至于序列化程序,您需要为每种类型编写一个不同的序列化程序,假设类型的字段不同。
  • 对,默认值非常具体。数组或 Collection 类型被序列化为 JSON 数组。 String 被序列化为 JSON 字符串,Number 类型被序列化为 JSON 数字,null 被序列化为 JSON null,其他引用类型被序列化为 JSON 对象。您正在尝试将引用类型 (Model) 序列化为具有单个元素的 JSON 数组。这是非常不标准的。
  • 嵌套 POJO 怎么样?这些将如何序列化?

标签: java json spring spring-mvc serialization


【解决方案1】:

看看Apache BeanUtils library,特别注意BeanUtils.populate()方法。

该方法的作用是根据 JavaBeans 约定将任何给定的Object 转换为Map&lt;String, Object&gt;。在键中,您将拥有属性名称,而在值中,您将拥有每个属性的值。对于标准情况,该方法应该足够了。仔细阅读文档,了解如何处理特殊情况。

Model model = ...; // get your model from some place

Map<String, Object> properties = new HashMap<>();

BeanUtils.populate(model, properties);

// Exception handling and special cases left as an excercise

上面递归地填充了properties 映射,这意味着如果您的Model 有一个名为otherModel 的属性,其类型为OtherModel,那么properties 映射将在与otherModel 键,其他嵌套 POJO 以此类推。

一旦您有了properties 映射,您想要序列化为数组元素的内容将在其值中。所以,这样的事情应该可以完成:

public List<Object> toArray(Map<String, Object> properties) {
    List<Object> result = new ArrayList<>();

    for (Object obj : properties.values()) {
        Object elem = null;
        if (obj != null) {
            Class<?> clz = obj.getClass();
            if (Map.class.isAssignableFrom(clz)) {
                elem = toArray((Map<String, Object>) obj); // recursion!
            } else {
                elem = obj;
            }
        }
        result.add(elem); // this adds null values
                          // move 1 line up if you don't
                          // want to serialize nulls
    }
    return result;
}

然后,在调用 toArray() 方法后,您将拥有一个准备好使用标准 Spring 机制序列化的 List&lt;Object&gt;。我什至相信您不需要特定的序列化程序:

List<Object> array = toArray(properties);
return array; // return this array, i.e. from a Controller 

免责声明:

请将此作为指南,而不是作为最终解决方案。我尽量小心,但代码可能有错误。我很确定它需要对数组和 POJO 的Iterables 进行特殊处理。毫无疑问,它缺乏异常处理。它仅适用于 POJO。如果提供的对象具有循环引用,它可能会爆炸。未经测试!

【讨论】:

    【解决方案2】:

    您可以为此使用@JsonValue 注释。 示例:

    public class Model {
    
      private int id;
    
      public Model(){}
    
      public Model(int id){
        this.id = id;
      }
    
      @JsonValue
      public int[] getValue() {
        return new int[]{this.id};
      } 
    }
    

    【讨论】:

    • 这适用于一个特定的实例。我已经更新了问题以澄清我正在寻找更通用的解决方案。谢谢!
    • 在这种情况下,我会使用 mixin 注释 (wiki.fasterxml.com/JacksonMixInAnnotations)。您可以创建一个带有 @JsonValue 注释的 value 方法的接口(或抽象类),并在所有类中实现此 mixin 接口。此外,您只需在 ObjectMapper 的配置中指定此 mixin,而无需实际更改您自己的类。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-11-17
    • 1970-01-01
    • 1970-01-01
    • 2014-03-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多