使用Jackson将JSON数组反序列化为单个Java对象

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用Jackson将JSON数组反序列化为单个Java对象相关的知识,希望对你有一定的参考价值。

我的想法是,我想将JSON数组["foo", "bar"]转换为Java对象,因此我需要通过索引将每个数组元素映射到property。

假设我有以下JSON:

{
  "persons": [
    [
      "John",
      "Doe"
    ],
    [
      "Jane",
      "Doe"
    ]
  ]
}

正如您所看到的,每个人只是一个数组,其中第一个名称是索引为0的元素,而最后一个名称是索引为1的元素。

我想将它反序列化为List<Person>。我使用mapper如下:

mapper.getTypeFactory().constructCollectionType(List.class, Person.class)

其中Person.class是:

public class Person {
    public final String firstName;
    public final String lastName;

    @JsonCreator
    public Person(@JsonProperty() String firstName, @JsonProperty String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }
}

我想知道我是否可以某种方式将数组索引指定为@JsonProperty参数而不是它的关键名称?

答案

感谢bureaquete建议使用自定义反序列化器。但它更适合我用SimpleModule而不是@JsonDeserialize注释注册它。下面是完整的JUnit测试示例:

@RunWith(JUnit4.class)
public class MapArrayToObjectTest {
    private static ObjectMapper mapper;

    @BeforeClass
    public static void setUp() {
        mapper = new ObjectMapper();
        SimpleModule customModule = new SimpleModule("ExampleModule", new Version(0, 1, 0, null));
        customModule.addDeserializer(Person.class, new PersonDeserializer());
        mapper.registerModule(customModule);
    }

    @Test
    public void wrapperDeserializationTest() throws IOException {
        //language=JSON
        final String inputJson = "{\"persons\": [[\"John\", \"Doe\"], [\"Jane\", \"Doe\"]]}";
        PersonsListWrapper deserializedList = mapper.readValue(inputJson, PersonsListWrapper.class);
        assertThat(deserializedList.persons.get(0).lastName, is(equalTo("Doe")));
        assertThat(deserializedList.persons.get(1).firstName, is(equalTo("Jane")));
    }

    @Test
    public void listDeserializationTest() throws IOException {
        //language=JSON
        final String inputJson = "[[\"John\", \"Doe\"], [\"Jane\", \"Doe\"]]";
        List<Person> deserializedList = mapper.readValue(inputJson, mapper.getTypeFactory().constructCollectionType(List.class, Person.class));
        assertThat(deserializedList.get(0).lastName, is(equalTo("Doe")));
        assertThat(deserializedList.get(1).firstName, is(equalTo("Jane")));
    }
}

class PersonsListWrapper {
    public List<Person> persons;
}

class Person {
    final String firstName;
    final String lastName;

    Person(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }
}

class PersonDeserializer extends JsonDeserializer<Person> {
    @Override
    public Person deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
        JsonNode node = jp.readValueAsTree();
        return new Person(node.get(0).getTextValue(), node.get(1).getTextValue());
    }
}

请注意,如果您不需要包装器对象,则可以使用mapper将JSON数组[["John", "Doe"], ["Jane", "Doe"]]直接反序列化为List<Person>,如下所示:

List<Person> deserializedList = mapper.readValue(inputJson, mapper.getTypeFactory().constructCollectionType(List.class, Person.class));
另一答案

它很容易序列化,但不容易以这种方式反序列化;

可以使用@JsonValue将以下类序列化为字符串数组;

public class Person {

    private String firstName;
    private String lastName;

    //getter,setter,constructors

    @JsonValue
    public List<String> craeteArr() {
        return Arrays.asList(this.firstName, this.lastName);
    }
}

但是要反序列化,我必须创建一个包装类,并使用@JsonDeserialize自定义反序列化;

public class PersonWrapper {

    @JsonDeserialize(using = CustomDeserializer.class)
    private List<Person> persons;

    //getter,setter,constructors
}

和自定义反序列化器本身;

public class CustomDeserializer extends JsonDeserializer<List<Person>> {

    @Override
    public List<Person> deserialize(JsonParser jsonParser, DeserializationContext context) throws IOException {
        JsonNode node = jsonParser.readValueAsTree();
        ObjectMapper mapper = new ObjectMapper();
        return IntStream.range(0, node.size()).boxed()
                .map(i -> {
                    try {
                        List<String> values = mapper.readValue(node.get(i).toString(), List.class);
                        return new Person().setFirstName(values.get(0)).setLastName(values.get(1));
                    } catch (IOException e) {
                        throw new RuntimeException();
                    }
                }).collect(Collectors.toList());
    }
}

您需要在反序列化器逻辑中进行适当的验证,以检查每个迷你数组是否包含正好两个值,但这很有效。

我宁愿使用这些步骤,也许隐藏@JsonDeserialize,我会做以下事情;

@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonDeserialize(using = CustomDeserializer.class)
public @interface AcceptPersonAsArray {}

所以你可以在PersonWrapper中使用一些自定义注释

public class PersonWrapper {

    @AcceptPersonAsArray
    private List<Person> persons;

    //getter,setter,constructors
}

以上是关于使用Jackson将JSON数组反序列化为单个Java对象的主要内容,如果未能解决你的问题,请参考以下文章

使用 Jackson 和 Spring 将 JavaScript 数组反序列化为 Java LinkedHashSet 不会删除重复项

如何使用 Jackson 将原始 JSON 反序列化为 Java 对象

使用 Jackson 将 JSON 反序列化为 ArrayList<POJO>

Jackson 将日期字符串反序列化为 Long

在将 json 反序列化为对象时,使用 jackson 将 asp.net / MS 专有 json Dateformat 转换为 java8 LocalDateTime

Jackson 将额外字段反序列化为 Map