【问题标题】:How to rename root key in JSON serialization with Jackson如何使用 Jackson 重命名 JSON 序列化中的根键
【发布时间】:2013-04-10 21:23:17
【问题描述】:

我正在使用 Jackson 对对象列表进行 JSON 序列化。

这是我得到的:

{"ArrayList":[{"id":1,"name":"test name"}]}

但我想要这个:

{"rootname":[{"id":1,"name":"test name"}]} // ie showing the string I want as the root name.

以下是我的处理方法:

界面:

public interface MyInterface {
    public long getId();
    public String getName();
}

实现类:

@JsonRootName(value = "rootname")
public class MyImpl implements MyInterface {
    private final long id;
    private String name;

    public MyImpl(final long id,final name) {
        this.id = id;
        this.name = name;
    }

   // getters     
}

JSON 序列化:

public class MySerializer {
    public static String serializeList(final List<MyInterface> lists) {
        //check for null value.Throw Exception
        final ObjectMapper mapper = new ObjectMapper();
        mapper.configure(SerializationConfig.Feature.WRAP_ROOT_VALUE, true);
        return mapper.writeValueAsString(lists);
    }
}

测试:

final List<MyInterface> list = new ArrayList<MyImpl>();
MyImpl item = new MyImpl(1L,"test name");
list.add(item);
final String json = MySerializer.serializeList(list);
System.out.println(json);

这是我得到的:

{"ArrayList":[{"id":1,"name":"test name"}]}

但我想要这个:

{"rootname":[{"id":1,"name":"test name"}]} // ie showing the string I want as the root     name.

我已经尝试了所有可以找到的建议解决方案,但未能实现我的目标。我看过:

或者我错过了什么?我为此使用杰克逊 1.9.12。欢迎在这方面提供任何帮助。

【问题讨论】:

  • 尝试序列化 MyImpl 的实例而不是 MyImpl 的列表。序列化程序正在做他的工作,因为他包装的是 List 而不是 MyInterface 对象。

标签: java json jackson root


【解决方案1】:

好吧,默认情况下,Jackson 在尝试确定要为包装值显示的根名称时使用两个注释之一 - @XmlRootElement@JsonRootName。它期望这个注解在被序列化的类型上,否则它将使用该类型的simple name 作为根名称。

在您的情况下,您正在序列化一个列表,这就是为什么根名称是“ArrayList”(被序列化类型的简单名称)。列表中的每个元素都可能属于带有 @JsonRootName 注释的类型,但列表本身不是

当您尝试包装的根值是一个集合时,您需要某种方式来定义包装名称:

Holder/Wrapper 类

您可以创建一个包装类来保存列表,并使用注释来定义所需的属性名称(您只需要在您无法直接控制ObjectMapper/JSON 转换过程时使用此方法):

class MyInterfaceList {
    @JsonProperty("rootname")
    private List<MyInterface> list;

    public List<MyInterface> getList() {
        return list;
    }

    public void setList(List<MyInterface> list) {
        this.list = list;
    }
}

final List<MyInterface> lists = new ArrayList<MyInterface>(4);
lists.add(new MyImpl(1L, "test name"));
MyInterfaceList listHolder = new MyInterfaceList();
listHolder.setList(lists);
final String json = mapper.writeValueAsString(listHolder);

对象编写器

这是最好的选择。使用已配置的 ObjectWriter 实例生成 JSON。特别是,我们对withRootName 方法感兴趣:

final List<MyInterface> lists = new ArrayList<MyInterface>(4);
lists.add(new MyImpl(1L, "test name"));
final ObjectWriter writer = mapper.writer().withRootName("rootName");
final String json = writer.writeValueAsString(lists);

【讨论】:

  • 对于创建 objectmapper 并将其传递给 3rd 方库的情况,一般如何实现 ObjectWriter 案例?我看不出有什么办法告诉杰克逊“一直使用这个对象编写器来处理这些类型”。
  • 一行完成所有操作new ObjectMapper().enable(SerializationFeature.WRAP_ROOT_VALUE).writer().withRootName("rootName").writeValueAsString(lists)
【解决方案2】:

我知道,我迟到了,但我有更好的方法,不需要 Holder/Wrapper 类。它从注释中选择根键。

package com.test;

import com.fasterxml.jackson.annotation.JsonRootName;


@JsonRootName("Products")
public class ProductDTO {
    private String name;
    private String description;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }
}

这里是测试类:-

package com.test;

import java.io.IOException;
import java.util.ArrayList;

import org.junit.Test;

import com.fasterxml.jackson.annotation.JsonRootName;
import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;


public class ProductDTOTestCase {
    @Test
    public void testPersistAndFindById() throws JsonGenerationException, JsonMappingException, IOException {
        ObjectMapper mapper = new ObjectMapper();
        ProductDTO productDTO = new ProductDTO();
        productDTO.setDescription("Product 4 - Test");

        ArrayList<ProductDTO> arrayList = new ArrayList<ProductDTO>();
        arrayList.add(productDTO);

        String rootName = ProductDTO.class.getAnnotation(JsonRootName.class).value();
        System.out.println(mapper.writer().withRootName(rootName).writeValueAsString(arrayList));

    }


}

它会给出以下输出

{"Products":[{"name":null,"description":"Product 4 - Test"}]}

【讨论】:

    【解决方案3】:
    @JsonTypeName("usuarios")
    @JsonTypeInfo(include= JsonTypeInfo.As.WRAPPER_OBJECT,use= JsonTypeInfo.Id.NAME)
    public class UsuarioDT extends ArrayList<Usuario> {
    
        @JsonProperty("rowsAffected")
        private Integer afectados;
    
        public Integer getAfectados() {
            return afectados;
        }
    
        public void setAfectados(Integer afectados) {
            this.afectados = afectados;
        }
    }
    

    【讨论】:

    • 请考虑添加关于为什么这就是答案的解释。
    • 非常棒的答案++
    • 它就像一个魅力!谢谢@JoseMontestruque
    • 使用了这个,现在我必须更改 10 个微服务端点,呵呵,谢谢
    【解决方案4】:

    你需要在类的顶部使用这个注解

    @JsonTypeName("rootname")

    【讨论】:

    • 如果您指的是stackoverflow.com/a/43638999/8857427,那么确实不是必需的,但输出将是第一个字母为大写的类名。 (即 UsuarioDT)。要消除这种情况,您可以使用 @JsonTypeName("usuarioDT")。
    猜你喜欢
    • 2011-01-26
    • 1970-01-01
    • 2012-02-02
    • 1970-01-01
    • 1970-01-01
    • 2012-03-17
    • 2021-01-06
    • 1970-01-01
    相关资源
    最近更新 更多