Załóżmy, że wywołuję interfejs API, który odpowiada następującym kodem JSON dla produktu:
{
"id": 123,
"name": "The Best Product",
"brand": {
"id": 234,
"name": "ACME Products"
}
}
Jestem w stanie zmapować identyfikator i nazwę produktu, używając adnotacji Jacksona:
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;
}
}
A następnie używając metody fromJson do stworzenia produktu:
JsonNode apiResponse = api.getResponse();
Product product = Json.fromJson(apiResponse, Product.class);
Ale teraz próbuję wymyślić, jak pobrać nazwę marki, która jest zagnieżdżoną właściwością. Miałem nadzieję, że coś takiego zadziała:
@JsonProperty("brand.name")
public String getBrandName() {
return brandName;
}
Ale oczywiście tak się nie stało. Czy istnieje łatwy sposób na osiągnięcie tego, co chcę, za pomocą adnotacji?
Rzeczywista odpowiedź JSON, którą próbuję przeanalizować, jest bardzo złożona i nie chcę tworzyć całej nowej klasy dla każdego węzła podrzędnego, mimo że potrzebuję tylko jednego pola.
Odpowiedzi:
Możesz to osiągnąć w ten sposób:
String brandName; @JsonProperty("brand") private void unpackNameFromNestedObject(Map<String, String> brand) { brandName = brand.get("name"); }
źródło
this.abbreviation = ((Map<String, Object>)portalCharacteristics.get("icon")).get("ticker").toString();
Oto jak poradziłem sobie z tym problemem:
Brand
klasa:package org.answer.entity; public class Brand { private Long id; private String name; public Brand() { } //accessors and mutators }
Product
klasa: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 }
W tym
brand
przypadku instancja zostanie zignorowana przezJackson
podczasserialization
ideserialization
, ponieważ jest opatrzona adnotacją@JsonIgnore
.Jackson
użyje metody z adnotacją@JsonGetter
dlaserialization
obiektu java doJSON
formatu. Tak więcbrandName
jest ustawiony zbrand.getName()
.Podobnie,
Jackson
użyje metody z adnotacją@JsonSetter
fordeserialization
ofJSON
format do obiektu java. W tym scenariuszu będziesz musiał samodzielnie utworzyć wystąpieniebrand
obiektu i ustawić jegoname
właściwość zbrandName
.Możesz użyć
@Transient
adnotacji trwałości zbrandName
, jeśli chcesz, aby była ignorowana przez dostawcę trwałości.źródło
Za pomocą wyrażeń JsonPath można mapować zagnieżdżone właściwości. Nie sądzę, aby było jakieś oficjalne wsparcie (zobacz ten problem), ale jest nieoficjalna implementacja tutaj: https://github.com/elasticpath/json-unmarshaller
źródło
Najlepiej jest używać metod ustawiających:
JSON:
... "coordinates": { "lat": 34.018721, "lng": -118.489090 } ...
metoda ustawiająca dla lat lub lng będzie wyglądać następująco:
@JsonProperty("coordinates") public void setLng(Map<String, String> coordinates) { this.lng = (Float.parseFloat(coordinates.get("lng"))); }
jeśli chcesz przeczytać oba (tak jak zwykle), użyj niestandardowej metody
@JsonProperty("coordinates") public void setLatLng(Map<String, String> coordinates){ this.lat = (Float.parseFloat(coordinates.get("lat"))); this.lng = (Float.parseFloat(coordinates.get("lng"))); }
źródło
Aby to uprościć… napisałem kod… większość z niego jest oczywista.
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]]
źródło
Cześć, oto cały działający kod.
// KLASA TESTÓW JUNIT
klasa publiczna 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 of 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
Kompletna treść: https://gist.github.com/jeorfevre/7c94d4b36a809d4acf2f188f204a8058
źródło