Digamos que estoy haciendo una llamada a una API que responde con el siguiente JSON para un producto:
{
  "id": 123,
  "name": "The Best Product",
  "brand": {
     "id": 234,
     "name": "ACME Products"
  }
}
Puedo mapear la identificación del producto y el nombre muy bien usando las anotaciones de Jackson:
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;
    }
}
Y luego usando el método fromJson para crear el producto:
  JsonNode apiResponse = api.getResponse();
  Product product = Json.fromJson(apiResponse, Product.class);
Pero ahora estoy tratando de averiguar cómo tomar el nombre de la marca, que es una propiedad anidada. Esperaba que algo como esto funcionara:
    @JsonProperty("brand.name")
    public String getBrandName() {
        return brandName;
    }
Pero, por supuesto, no fue así. ¿Existe una manera fácil de lograr lo que quiero usando anotaciones?
La respuesta JSON real que estoy tratando de analizar es muy compleja y no quiero tener que crear una clase completamente nueva para cada subnodo, aunque solo necesito un campo.

Respuestas:
Puedes lograr esto así:
String brandName; @JsonProperty("brand") private void unpackNameFromNestedObject(Map<String, String> brand) { brandName = brand.get("name"); }fuente
this.abbreviation = ((Map<String, Object>)portalCharacteristics.get("icon")).get("ticker").toString();Así es como manejé este problema:
Brandclase:package org.answer.entity; public class Brand { private Long id; private String name; public Brand() { } //accessors and mutators }Productclase: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 }Aquí, la
brandinstancia será ignorada porJacksonduranteserializationydeserialization, ya que está anotada con@JsonIgnore.Jacksonutilizará el método anotado con@JsonGetterforserializationof java object enJSONformato. Entonces, elbrandNameestá configurado conbrand.getName().De manera similar,
Jacksonusará el método anotado con@JsonSetterfordeserializationofJSONformat en el objeto java. En este escenario, deberá crear una instancia delbrandobjeto usted mismo y establecer sunamepropiedad desdebrandName.Puede utilizar
@Transientla anotación de persistencia conbrandName, si desea que el proveedor de persistencia la ignore.fuente
Puede utilizar expresiones JsonPath para asignar propiedades anidadas. No creo que haya ningún soporte oficial (vea este problema), pero hay una implementación no oficial aquí: https://github.com/elasticpath/json-unmarshaller
fuente
Lo mejor es utilizar métodos de establecimiento:
JSON:
... "coordinates": { "lat": 34.018721, "lng": -118.489090 } ...El método setter para lat o lng se verá así:
@JsonProperty("coordinates") public void setLng(Map<String, String> coordinates) { this.lng = (Float.parseFloat(coordinates.get("lng"))); }si necesita leer ambos (como lo haría normalmente), utilice un método personalizado
@JsonProperty("coordinates") public void setLatLng(Map<String, String> coordinates){ this.lat = (Float.parseFloat(coordinates.get("lat"))); this.lng = (Float.parseFloat(coordinates.get("lng"))); }fuente
Para hacerlo simple ... he escrito el código ... la mayor parte se explica por sí mismo.
Main Methodpackage 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 ProductTestpackage 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 BrandNamepackage 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 + "]"; } }OUTPUTProductTest [productId=123, productName=The Best Product, brandName=BrandName [id=234, name=ACME Products]]fuente
Hola, aquí está el código de trabajo completo.
// CLASE DE PRUEBA JUNIT
public class sof {
@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; }//Console.log de junit testcase
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:49Resumen completo: https://gist.github.com/jeorfevre/7c94d4b36a809d4acf2f188f204a8058
fuente