Jackson 주석을 사용하여 중첩 된 값을 속성에 매핑하는 방법은 무엇입니까?


90

제품에 대해 다음 JSON으로 응답하는 API를 호출한다고 가정 해 보겠습니다.

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

그런 다음 fromJson 메서드를 사용하여 제품을 만듭니다.

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

하지만 이제는 중첩 된 속성 인 브랜드 이름을 파악하는 방법을 알아 내려고합니다. 나는 이와 같은 것이 효과가 있기를 바랐습니다.

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

그러나 물론 그렇지 않았습니다. 주석을 사용하여 원하는 작업을 쉽게 수행 할 수 있습니까?

구문 분석하려는 실제 JSON 응답은 매우 복잡하며 단일 필드 만 필요하더라도 모든 하위 노드에 대해 전체 새 클래스를 만들 필요가 없습니다.


2
결국 github.com/json-path/JsonPath를 사용하게되었습니다. Spring은 내부적으로도 사용합니다. 예를 들어, org.springframework.data.web.
dehumanizer

답변:


89

다음과 같이이를 달성 할 수 있습니다.

String brandName;

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

23
3 층 깊이는 어떻습니까?
Robin Kanters

5
@Robin 작동하지 않을까요? this.abbreviation = ((Map<String, Object>)portalCharacteristics.get("icon")).get("ticker").toString();
Marcello de Sales

같은 방법으로 잘 ... unpackValuesFromNestedBrand로 여러 값을 풀고 사용할 수 있습니다 - 감사합니다
msanjay

13

이것이 내가이 문제를 처리 한 방법입니다.

Brand 수업:

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인스턴스는으로 주석 처리되었으므로 및 Jackson동안에 의해 무시됩니다 .serializationdeserialization@JsonIgnore

Jackson@JsonGetterfor serialization자바 객체의 주석이 붙은 메소드 를 JSON형식 으로 사용 합니다. 그래서 brandName는로 설정됩니다 brand.getName().

유사하게, Jackson주석이있어서 사용 @JsonSetter을 위해 deserializationJSON자바 객체로 포맷한다. 이 시나리오에서는 brand개체를 직접 인스턴스화 하고 다음에서 name속성을 설정해야 합니다.brandName .

지속성 제공자가 무시하려는 경우에서 @Transient지속성 주석을 사용할 수 있습니다 brandName.



1

가장 좋은 방법은 setter 메서드를 사용하는 것입니다.

JSON :

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

lat 또는 lng에 대한 setter 메서드는 다음과 같습니다.

 @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")));
}

-6

간단하게 .. 코드를 작성했습니다. 대부분은 설명이 필요하지 않습니다.

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
이것은 작동하지만 하나의 필드 만 필요하더라도 모든 노드에 대해 새 클래스를 만들 필요가없는 솔루션을 찾으려고합니다.
kenske

-7

안녕하세요, 완전한 작업 코드입니다.

// JUNIT 테스트 클래스

공개 클래스 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;     
}

// junit 테스트 케이스의 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 

전체 요점 : https://gist.github.com/jeorfevre/7c94d4b36a809d4acf2f188f204a8058


1
매핑하려는 실제 JSON 응답은 매우 복잡합니다. 많은 노드와 하위 노드가 있으며 각각에 대한 클래스를 만드는 것은 매우 고통스럽기 때문에 대부분의 경우 3 중 중첩 노드 집합에서 단일 필드 만 필요합니다. 많은 새 클래스를 만들지 않고도 단일 필드를 얻을 수있는 방법이 없습니까?
kenske

새 sof 티켓을 열고 나와 공유해주세요. 제가 도와 드릴 수 있습니다. 매핑하고 싶은 json 구조를 공유 해주세요. :)
jeorfevre
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.