繁体   English   中英

使用 Jackson 将 JSON 数组解析为 Java 类

[英]Parse JSON array into Java classes using Jackson

我有一个相当复杂的 JSON 文件,我需要将其解析为多个 Java 类。

JSON的结构如下:


{
  "dataBlock": [
    {
      "headingName": {
        "name": "Operational Information",
        "position": "1",
        "attributes": [
          {
            "name": "Brand",
            "position": "1",
            "value": [
              "A",
              "B"
            ]
          },
          {
            "name": "Data Model Id",
            "position": "2",
            "value": "000001"
          }
        ]
      }
    },
    {
      "headingName": {
        "name": "CRA",
        "position": "6",
        "attributes": [
          {
            "name": "Company",
            "position": "1",
            "value": "private_limited_company"
          },
          {
            "name": "Address",
            "position": "3",
            "value": {
              "line1": "AAA",
              "line2": "BBB",
              "line3": "CCC",
              "line4": "DDD",
              "postalCode": "AB XYZ",
              "countryCode": "GBR"
            }
          }
        ]
      }
    }
  ]
}

我希望将每个“headingName”节点存储到一个单独的 Java Class (该结构将来可能会因数据块数组中的各个类别/标题名称而有所不同,例如我的回复是:

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
public class MyResponse {
    private OperationalInformation operationalInformation;
    private CRA cra;
    ...
}

我该如何做到这一点?

我的解决方案涉及两个阶段:

反序列化

将 json 反序列化为与输入 json 具有相同层次结构和结构的对象,这些对象包含 String 和 object 属性。 这组类可以支持任何和所有标题名称和属性

@Data
public abstract class JsonCommonProperties {
    protected String name;
    protected String position;
}

@Data
@ToString(callSuper = true)
public class JsonAttribute<T> extends JsonCommonProperties {
    private T value;
}

@Data
@ToString(callSuper = true)
public class JsonHeading extends JsonCommonProperties {
    private List<JsonAttribute<?>> attributes;
}

public class JsonResponse {
    
    public List<HeadingName> dataBlock;
    
    public static class HeadingName {
        public JsonHeading headingName;
    }
}

这组类可用于使用ObjectMapper.readValue()进行直接反序列化。

构建我的响应

将上述 object 层次结构转换为 MyResponse,使用反射和 3rd 方库来构建对象并从字符串名称填充实例变量。

我为所有标题使用了一个常见的超级 class。 下面指定了类集

@Getter
@Setter
@ToString
public abstract class Heading {
    private String name;

    public Heading(String name) {
        setName(name);
    }
}

@Getter
@Setter
@ToString(callSuper = true)
class CRA extends Heading {
    private String company;
    private Map<String, String> address;

    public CRA (String name) {
        super(name);
    }
}

@Getter
@Setter
@ToString(callSuper = true)
class OperationalInformation extends Heading {
    private List<String> brand;
    private String dataModelId;

    public OperationalInformation (String name) {
        super(name);
    }
}

@Data
public class MyResponse {
    private OperationalInformation operationalInformation;
    private CRA cra;
}

挑战和解决方案是

  1. convert heading name to type of heading: I used apache common StringUtils and apache text CaseUtils to convert json property to class name, and then used reflection to instantiate an instance

  2. JsonAttribute填充标题的实例变量:我使用Jodd Util库(更好的替代 apache BeanUtils )从字符串名称动态调用设置器。

我把上面的解决方案放在一个标题厂class

public class HeadingFactory {

    /**
     * instantiate concrete Heading from specified JsonHeading
     */
    @SuppressWarnings("unchecked")
    public static Heading getHeading(JsonHeading jsonHeading) throws ReflectiveOperationException {
        Class<Heading> concreteHeadingClass =
            (Class<Heading>)Class.forName(getMyResponsePackage() + "." + getHeadingType(jsonHeading));
        Heading heading = concreteHeadingClass.getConstructor(String.class).newInstance(jsonHeading.getName());
        populateHeading(heading, jsonHeading);
        return heading;
    }

    /**
     * return class name of concrete Heading (type of instance var in MyResponse)
     * from specified JsonHeading
     */
    public static String getHeadingType(JsonHeading jsonHeading) {
        // handle all uppercase heading, like CRA
        if (StringUtils.isAllUpperCase(jsonHeading.getName())) return jsonHeading.getName();
        // handle separate word heading, like Operational Information
        return CaseUtils.toCamelCase(jsonHeading.getName(), true, ' ');
    }

    private static String getMyResponsePackage() {
        return StringUtils.substringBeforeLast(MyResponse.class.getName(), ".");
    }

    // use jodd util to dynamically call setter methods of heading
    // and pass values of jsonAttributes of jsonHeading
    private static void populateHeading(Heading heading, JsonHeading jsonHeading) {
        jsonHeading.getAttributes().forEach(jsonAttribute ->
            BeanUtil.pojo.setProperty(heading, jsonAttribute.getPropertyName(), jsonAttribute.getValue())
        );
    }
}

使用相同的技术和库填充 MyResponse 的实例变量。 这是我的主要方法

public static void main(String[] args) {
    ObjectMapper mapper = new ObjectMapper();
    try (BufferedReader br = Files.newBufferedReader(Paths.get("C://temp/json.json"))) {
        JsonResponse jsonResponse = mapper.readValue(br, JsonResponse.class);
        MyResponse myResponse = new MyResponse();
        for (JsonResponse.HeadingName headingName : jsonResponse.dataBlock) {
            Heading heading = HeadingFactory.getHeading(headingName.headingName);
            String myResponseProperty = headingName.headingName.getPropertyName();
            BeanUtil.pojo.setProperty(myResponse, myResponseProperty, heading);
        }
        System.out.println(myResponse);

    } catch (Exception e) {
        e.printStackTrace();
    }
}

在反序列化为 POJO 之前,使用 JSON 查询语言转换 JSON 结构。 一条Josson查询语句就可以完成这项工作。

dataBlock.map(headingName.name.camelCase()::headingName.field(name:)).mergeObjects()

https://github.com/octomix/josson

演示

Josson josson = Josson.fromJsonString(
    "{" +
    "  \"dataBlock\": [ {" +
    "    \"headingName\": {" +
    "      \"name\": \"Operational Information\"," +
    "      \"position\": \"1\"," +
    "      \"attributes\": [ {" +
    "          \"name\": \"Brand\"," +
    "          \"position\": \"1\"," +
    "          \"value\": [\"A\",\"B\"]" +
    "      }," +
    "      {" +
    "        \"name\": \"Data Model Id\"," +
    "        \"position\": \"2\"," +
    "        \"value\": \"000001\"" +
    "      } ]" +
    "    }" +
    "  }," +
    "  {" +
    "    \"headingName\": {" +
    "      \"name\": \"CRA\"," +
    "      \"position\": \"6\"," +
    "      \"attributes\": [ {" +
    "        \"name\": \"Company\"," +
    "        \"position\": \"1\"," +
    "        \"value\": \"private_limited_company\"" +
    "      }," +
    "      {" +
    "        \"name\": \"Address\"," +
    "        \"position\": \"3\"," +
    "        \"value\": {" +
    "          \"line1\": \"AAA\"," +
    "          \"line2\": \"BBB\"," +
    "          \"line3\": \"CCC\"," +
    "          \"line4\": \"DDD\"," +
    "          \"postalCode\": \"AB XYZ\"," +
    "          \"countryCode\": \"GBR\"" +
    "        }" +
    "      } ]" +
    "    }" +
    "  } ]" +
    "}");
JsonNode node = josson.getNode(
    "dataBlock.map(headingName.name.camelCase()::headingName.field(name:)).mergeObjects()");
System.out.println(node.toPrettyString());

Output

{
  "operationalInformation" : {
    "position" : "1",
    "attributes" : [ {
      "name" : "Brand",
      "position" : "1",
      "value" : [ "A", "B" ]
    }, {
      "name" : "Data Model Id",
      "position" : "2",
      "value" : "000001"
    } ]
  },
  "cra" : {
    "position" : "6",
    "attributes" : [ {
      "name" : "Company",
      "position" : "1",
      "value" : "private_limited_company"
    }, {
      "name" : "Address",
      "position" : "3",
      "value" : {
        "line1" : "AAA",
        "line2" : "BBB",
        "line3" : "CCC",
        "line4" : "DDD",
        "postalCode" : "AB XYZ",
        "countryCode" : "GBR"
      }
    } ]
  }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM