【问题标题】:How to transform a flat JSON to hierarchical java Class?如何将平面 JSON 转换为分层 java 类?
【发布时间】:2019-06-20 12:17:44
【问题描述】:

我需要将平面 JSON 对象反序列化为 Java 对象,并将一些属性设置为子对象。

{
 "name": "abcd",
 "addressLine1": "123",
 "addressLine2": "1111"
}

Class Student {
  String name;
  Address address;
}

Class Address {
 String line1;
 String line2;
}

如何使用 Jackson 将我的 JSON 反序列化为 Student 对象? 我无法映射addressLine1 to Student.Address.line1addressLine2 to Student.Address.line2

【问题讨论】:

  • 你确定json? name、addressLine1 和 addressLine2 不在双引号之间吗? “1111”后的逗号也使其无效。

标签: java json jackson jackson-databind


【解决方案1】:

编辑:虽然我的解决方案有效,但 Selindek 的答案是最好的

根据https://jsonlint.com/,您的 Json 无效,原因有两个:

  • 您的字段名称没有被引用
  • 最后一行后面有一个逗号

我将假设这个 JSON,带有不带引号的字段名称:

{
    name: "abcd",
    addressLine1: "123",
    addressLine2: "1111"
}

我可以想到两种方法:

1 - 简单的地图处理

// Create your mapper, and configure it to allow unquoted field names
ObjectMapper mapper = new ObjectMapper();
mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);

// Parse the JSON to a Map
TypeReference<HashMap<String, String>> typeRef
        = new TypeReference<HashMap<String, String>>() {};
Map<String, String> jsonAsMap = null;
try {
    jsonAsMap = mapper.readValue(yourJsonString, typeRef);
} catch (Exception e) {
    System.out.println("Something went wrong:" + e.getMessage());
}


// Read the data from the map and build your objects
Student student = null;
if(jsonAsMap != null) {

    Address address = new Address();
    address.setLine1(jsonAsMap.get("addressLine1"));
    address.setLine2(jsonAsMap.get("addressLine2"));

    student = new Student();
    student.setName(jsonAsMap.get("name"));
    student.setAddress(address);

    System.out.println(student.getName());
    System.out.println(student.getAddress().getLine1());
    System.out.println(student.getAddress().getLine2());
}

2 - 使用代理对象(我更喜欢这个)

另一种方法是拥有一个代理类,您可以在其中反序列化您的 JSON,并从中构建您的学生:

class RawStudent {
    private String name, addressLine1, addressLine2;

    public Student toStudent() {
        Address address = new Address();
        address.setLine1(addressLine1);
        address.setLine2(addressLine2);

        Student student = new Student();
        student.setName(name);
        student.setAddress(address);

        return student;
    }

    // GETTERS / SETTERS

}

并以这种方式使用它:

// Create your mapper, and configure it to allow unquoted field names
ObjectMapper mapper = new ObjectMapper();
mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);

// Parse the JSON to a RawStudent object
RawStudent rawStudent = null;
try {
    rawStudent = mapper.readValue(jsonUnquoted, RawStudent.class);
} catch (Exception e) {
    System.out.println("Something went wrong:" + e.getMessage());
}


// Read the data from the map and build your objects
Student student = null;
if (rawStudent != null) {

    student = rawStudent.toStudent();

    System.out.println(student.getName());
    System.out.println(student.getAddress().getLine1());
    System.out.println(student.getAddress().getLine2());
}

注意

如果您输入错误并且确实有引用的字段,即:

{
    "name": "abcd",
    "addressLine1": "123",
    "addressLine2": "1111"
}

那你就不需要那行了

mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);

【讨论】:

    【解决方案2】:

    你可以这样定义你的数据类:

      public static class Student {
        String name;
    
        @JsonUnwrapped
        Address address;
      }
    
      public static class Address {
        @JsonProperty("addressLine1")
        String line1;
        @JsonProperty("addressLine2")
        String line2;
      }
    

    然后您可以按通常的方式使用Objectmapper - 无需任何额外的魔法或解决方法:

    Student student = mapper.readValue(json, Student.class);
    

    如果您传入的 json 字符串确实是您提供的格式(不带引号),那么还要添加:

    mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
    

    【讨论】:

    • 不错!不知道那个 @JsonUnwrapped 注释。我认为您需要用@JsonProperty 注释name 属性。
    • 也可以对name属性进行注解,不过没必要,因为类中的属性名和json字符串中的属性名是一样的。
    • 我刚刚提出了这个问题,因为我试图运行它,但没有注释它对我不起作用。
    • 这很有趣。我在发布答案之前尝试过它,它对我的​​ fastxml 2.9.8 有效。也许您使用了具有不同默认设置的其他包或版本。
    • 我想这并不重要,反正这不是问题所在:)
    【解决方案3】:

    你可以像这样使用 ObjectMapper:

    ObjectMapper objectMapper = new ObjectMapper();
    Student student = objectMapper.readValue(json, Student.class);
    

    但您还必须修改 Student 类,并实现所有 getter 和 setter:

    Class Student{
        String name;
        String addressLine1;
        String addressLine2;
    } 
    

    然后,如果您愿意,可以根据需要将其重构为新类。希望能帮助到你。

    【讨论】:

      【解决方案4】:

      首先,确保您的依赖项或库中有jackson-databind

      你可以这样做:

      String jsonInput = // You JSON String here;
      TypeReference<HashMap<String, String>> typeRef 
        = new TypeReference<HashMap<String, String>>() {};
      Map<String, String> map = mapper.readValue(jsonInput, typeRef);
      
      Student student = new Student();
      Address address = new Address();
      
      for (Map.Entry<String, String> entry : map.entrySet())
      {
          if(((String)entry.getKey()).equals("addressLine1")){
          student.setName(map.get("name"));
          student.setAddress(map.get("addressLine1"));
          }
      
          if(((String)entry.getKey()).equals("addressLine2")){
          address.setLine1(map.get("addressLine1"));
          address.setLine2(map.get("addressLine2"));
          }
          //System.out.println(entry.getKey() + "/" + entry.getValue());
      }
      

      类对象中的直接反序列化意味着类和 Json 字符串具有完全相同的属性。这不是这里的情况。因此使用 for 循环和使用 mutators。

      阅读this了解更多详情

      【讨论】:

        猜你喜欢
        • 2022-11-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-05-26
        相关资源
        最近更新 更多