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:
Brand
clase:package org.answer.entity; public class Brand { private Long id; private String name; public Brand() { } //accessors and mutators }
Product
clase: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
brand
instancia será ignorada porJackson
duranteserialization
ydeserialization
, ya que está anotada con@JsonIgnore
.Jackson
utilizará el método anotado con@JsonGetter
forserialization
of java object enJSON
formato. Entonces, elbrandName
está configurado conbrand.getName()
.De manera similar,
Jackson
usará el método anotado con@JsonSetter
fordeserialization
ofJSON
format en el objeto java. En este escenario, deberá crear una instancia delbrand
objeto usted mismo y establecer suname
propiedad desdebrandName
.Puede utilizar
@Transient
la 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 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]]
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:49
Resumen completo: https://gist.github.com/jeorfevre/7c94d4b36a809d4acf2f188f204a8058
fuente