[英]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;
}
挑战和解决方案是
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
从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.