如何使用 Jackson 注释将嵌套值映射到属性?

假设我调用了一个 API,它对产品响应以下 JSON:

{
"id": 123,
"name": "The Best Product",
"brand": {
"id": 234,
"name": "ACME Products"
}
}

我能够使用 Jackson 注释映射产品 ID 和名称:

public class ProductTest {
private int productId;
private String productName, brandName;


@JsonProperty("id")
public int getProductId() {
return productId;
}


public void setProductId(int productId) {
this.productId = productId;
}


@JsonProperty("name")
public String getProductName() {
return productName;
}


public void setProductName(String productName) {
this.productName = productName;
}


public String getBrandName() {
return brandName;
}


public void setBrandName(String brandName) {
this.brandName = brandName;
}
}

然后使用 from Json 方法创建产品:

  JsonNode apiResponse = api.getResponse();
Product product = Json.fromJson(apiResponse, Product.class);

但是现在我想知道如何获得品牌名称,这是一个嵌套的属性。我希望这样的方法能奏效:

    @JsonProperty("brand.name")
public String getBrandName() {
return brandName;
}

但是当然没有。有没有一种简单的方法可以使用注释来实现我想要的效果呢?

我试图解析的实际 JSON 响应非常复杂,我不想为每个子节点创建一个完整的新类,即使我只需要一个字段。

114569 次浏览

你好,这里是 完整的工作守则。

//JUNIT 测试班

公共类别的{

@Test
public void test() {


Brand b = new Brand();
b.id=1;
b.name="RIZZE";


Product p = new Product();
p.brand=b;
p.id=12;
p.name="bigdata";




//mapper
ObjectMapper o = new ObjectMapper();
o.registerSubtypes(Brand.class);
o.registerSubtypes(Product.class);
o.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);


String json=null;
try {
json = o.writeValueAsString(p);
assertTrue(json!=null);
logger.info(json);


Product p2;
try {
p2 = o.readValue(json, Product.class);
assertTrue(p2!=null);
assertTrue(p2.id== p.id);
assertTrue(p2.name.compareTo(p.name)==0);
assertTrue(p2.brand.id==p.brand.id);
logger.info("SUCCESS");
} catch (IOException e) {


e.printStackTrace();
fail(e.toString());
}








} catch (JsonProcessingException e) {


e.printStackTrace();
fail(e.toString());
}




}
}








**// Product.class**
public class Product {
protected int id;
protected String name;


@JsonProperty("brand") //not necessary ... but written
protected Brand brand;


}


**//Brand class**
public class Brand {
protected int id;
protected String name;
}

//junit testcase 的 Console.log

2016-05-03 15:21:42 396 INFO  {"id":12,"name":"bigdata","brand":{"id":1,"name":"RIZZE"}} / MReloadDB:40
2016-05-03 15:21:42 397 INFO  SUCCESS / MReloadDB:49

Complete gist : https://gist.github.com/jeorfevre/7c94d4b36a809d4acf2f188f204a8058

为了简单起见. . 我已经写了代码... 大部分是不言而喻的。

Main Method

package com.test;


import java.io.IOException;


import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;


public class LOGIC {


public static void main(String[] args) throws JsonParseException, JsonMappingException, IOException {


ObjectMapper objectMapper = new ObjectMapper();
String DATA = "{\r\n" +
"  \"id\": 123,\r\n" +
"  \"name\": \"The Best Product\",\r\n" +
"  \"brand\": {\r\n" +
"     \"id\": 234,\r\n" +
"     \"name\": \"ACME Products\"\r\n" +
"  }\r\n" +
"}";


ProductTest productTest = objectMapper.readValue(DATA, ProductTest.class);
System.out.println(productTest.toString());


}


}

Class ProductTest

package com.test;


import com.fasterxml.jackson.annotation.JsonProperty;


public class ProductTest {


private int productId;
private String productName;
private BrandName brandName;


@JsonProperty("id")
public int getProductId() {
return productId;
}
public void setProductId(int productId) {
this.productId = productId;
}


@JsonProperty("name")
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}


@JsonProperty("brand")
public BrandName getBrandName() {
return brandName;
}
public void setBrandName(BrandName brandName) {
this.brandName = brandName;
}


@Override
public String toString() {
return "ProductTest [productId=" + productId + ", productName=" + productName + ", brandName=" + brandName
+ "]";
}






}

Class BrandName

package com.test;


public class BrandName {


private Integer id;
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "BrandName [id=" + id + ", name=" + name + "]";
}








}

OUTPUT

ProductTest [productId=123, productName=The Best Product, brandName=BrandName [id=234, name=ACME Products]]

你可以这样做:

String brandName;


@JsonProperty("brand")
private void unpackNameFromNestedObject(Map<String, String> brand) {
brandName = brand.get("name");
}

可以使用 JsonPath 表达式映射嵌套属性。我不认为有任何官方的支持(见 这个问题) ,但有一个非官方的实现: https://github.com/elasticpath/json-unmarshaller

This is how I handled this problem:

Brand class:

package org.answer.entity;


public class Brand {


private Long id;


private String name;


public Brand() {


}


//accessors and mutators
}

Product级:

package org.answer.entity;


import com.fasterxml.jackson.annotation.JsonGetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonSetter;


public class Product {


private Long id;


private String name;


@JsonIgnore
private Brand brand;


private String brandName;


public Product(){}


@JsonGetter("brandName")
protected String getBrandName() {
if (brand != null)
brandName = brand.getName();
return brandName;
}


@JsonSetter("brandName")
protected void setBrandName(String brandName) {
if (brandName != null) {
brand = new Brand();
brand.setName(brandName);
}
this.brandName = brandName;
}


//other accessors and mutators
}

在这里,brand实例将在 serializationdeserialization期间被 Jackson忽略,因为它是用 @JsonIgnore注释的。

Jackson will use the method annotated with @JsonGetter for serialization of java object into JSON format. So, the brandName is set with brand.getName().

类似地,Jackson将使用用 @JsonSetter注释的方法将 JSON格式的 deserialization转换为 java 对象。在这个场景中,您必须自己实例化 brand对象,并从 brandName中设置它的 name属性。

如果希望持久性提供程序忽略 @Transient,可以对 brandName使用 @Transient持久性注释。

最好的方法是使用 setter 方法:

杰森:

...
"coordinates": {
"lat": 34.018721,
"lng": -118.489090
}
...

Setter 的 lat 或 lng 方法将类似于:

 @JsonProperty("coordinates")
public void setLng(Map<String, String> coordinates) {
this.lng = (Float.parseFloat(coordinates.get("lng")));
}

如果需要同时读取这两个函数(正常情况下) ,则使用自定义方法

@JsonProperty("coordinates")
public void setLatLng(Map<String, String> coordinates){
this.lat = (Float.parseFloat(coordinates.get("lat")));
this.lng = (Float.parseFloat(coordinates.get("lng")));
}

这是我对与 JSON 嵌套映射相关的 解决任何问题问题的解决方案。

首先将您的响应存储为 地图:

        ResponseEntity<Map<String, Object>> response =
restTemplate.exchange(
requestUri,
HttpMethod.GET,
buildAuthHeader(),
new ParameterizedTypeReference<>() {
});

现在你可以阅读 JSON 树中的任何嵌套值,例如:

response.getBody().get("content").get(0).get("terminal")

在这种情况下,我的 JSON 是:

{
"page": 0,
"total_pages": 1,
"total_elements": 2,
"content": [
{
"merchant": "000405",
"terminal": "38010101",
"status": "Para ser instalada",
....

Just use @JsonUnwrapped:

https://www.logicbig.com/tutorials/misc/jackson/json-unwrapped.html

这将子对象放置在父对象中。