Jackson ek açıklamalarını kullanarak yuvalanmış bir değeri bir özelliğe nasıl eşleyebilirim?


93

Bir ürün için aşağıdaki JSON ile yanıt veren bir API'ye çağrı yaptığımı varsayalım:

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

Ürün kimliğini ve adını Jackson ek açıklamalarını kullanarak iyi bir şekilde eşleyebiliyorum:

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;
    }
}

Ve sonra ürünü oluşturmak için fromJson yöntemini kullanarak:

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

Ama şimdi iç içe geçmiş bir mülk olan marka adını nasıl alacağımı bulmaya çalışıyorum. Bunun gibi bir şeyin işe yarayacağını umuyordum:

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

Ama tabii ki olmadı. Ek açıklamaları kullanarak istediğimi başarmanın kolay bir yolu var mı?

Ayrıştırmaya çalıştığım gerçek JSON yanıtı çok karmaşık ve sadece tek bir alana ihtiyacım olmasına rağmen her alt düğüm için tamamen yeni bir sınıf oluşturmak zorunda kalmak istemiyorum.


2
Ben kullanarak sona erdi github.com/json-path/JsonPath - Bahar yanı başlık altında bunu kullanır. Örneğin, org.springframework.data.web dosyasında.
dehumanizer

Yanıtlar:


91

Bunu şu şekilde başarabilirsiniz:

String brandName;

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

24
Üç seviye derinliğe ne dersiniz?
Robin Kanters

5
@Robin Bu işe yaramaz mı? this.abbreviation = ((Map<String, Object>)portalCharacteristics.get("icon")).get("ticker").toString();
Marcello de Sales

aynı yöntem birden çok değeri açmak için de kullanılabilir ... unpackValuesFromNestedBrand - teşekkürler
msanjay

13

Bu sorunu şu şekilde hallettim:

Brand sınıf:

package org.answer.entity;

public class Brand {

    private Long id;

    private String name;

    public Brand() {

    }

    //accessors and mutators
}

Product sınıf:

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
}

Burada, brandörnek tarafından göz ardı edilecektir Jacksonsırasında serializationve deserializationonunla açıklamalı olduğundan, @JsonIgnore.

Jackson@JsonGetterfor serializationof java nesnesinin JSONformata dönüştürülmesi için ek açıklamalı yöntemi kullanacaktır . Yani, brandNameile ayarlanır brand.getName().

Benzer şekilde, for of format Jacksonile açıklamalı yöntemi java nesnesine kullanacaktır . Bu senaryoda, nesneyi kendiniz başlatmanız ve özelliğini buradan ayarlamanız gerekir .@JsonSetterdeserializationJSONbrandnamebrandName

Kalıcılık sağlayıcısı tarafından göz ardı edilmesini istiyorsanız, @Transientkalıcılık ek açıklamasını kullanabilirsiniz brandName.



1

En iyisi, ayarlayıcı yöntemlerini kullanmaktır:

JSON:

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

lat veya lng için ayarlayıcı yöntemi şöyle görünecektir:

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

her ikisini de okumanız gerekiyorsa (normalde yaptığınız gibi) özel bir yöntem kullanın

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

-6

Basitleştirmek için .. Kodu yazdım ... çoğu kendinden açıklamalı.

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]]

6
Bu işe yarıyor, ancak tek bir alana ihtiyacım olsa bile her düğüm için yeni bir sınıf oluşturmak zorunda olmadığım bir çözüm bulmaya çalışıyorum.
kenske

-7

Merhaba, işte tam çalışma kodu.

// HAZİRAN TEST SINIFI

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 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 

Tam özet: https://gist.github.com/jeorfevre/7c94d4b36a809d4acf2f188f204a8058


1
Haritalamaya çalıştığım gerçek JSON yanıtı çok karmaşık. Çok sayıda düğümü ve alt düğümü vardır ve her biri için bir sınıf oluşturmak çok zahmetli olacaktır, hatta çoğu durumda bir dizi üçlü düğümden yalnızca tek bir alana ihtiyacım olduğunda. Bir ton yeni sınıf oluşturmak zorunda kalmadan tek bir alanı elde etmenin bir yolu yok mu?
kenske

yeni bir sof bilet açın ve benimle paylaşın, size bu konuda yardımcı olabilirim. Haritalamak istediğiniz json struture lütfen paylaşın. :)
jeorfevre
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.