Làm cách nào để ánh xạ một giá trị lồng vào một thuộc tính bằng cách sử dụng các chú thích của Jackson?


90

Giả sử tôi đang thực hiện cuộc gọi đến một API phản hồi bằng JSON sau cho một sản phẩm:

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

Tôi có thể lập bản đồ id và tên sản phẩm bằng cách sử dụng chú thích 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;
    }
}

Và sau đó sử dụng phương thức fromJson để tạo sản phẩm:

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

Nhưng bây giờ tôi đang cố gắng tìm ra cách lấy tên thương hiệu, một tài sản lồng vào nhau. Tôi đã hy vọng rằng một cái gì đó như thế này sẽ hoạt động:

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

Nhưng tất nhiên là không. Có cách nào dễ dàng để đạt được những gì tôi muốn bằng cách sử dụng chú thích không?

Phản hồi JSON thực tế mà tôi đang cố gắng phân tích cú pháp rất phức tạp và tôi không muốn phải tạo toàn bộ một lớp mới cho mọi nút con, mặc dù tôi chỉ cần một trường duy nhất.


2
Tôi đã kết thúc bằng cách sử dụng github.com/json-path/JsonPath - Spring cũng sử dụng nó. Ví dụ: trong org.springframework.data.web của họ.
dehumanizer

Câu trả lời:


89

Bạn có thể đạt được điều này như thế này:

String brandName;

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

23
Làm thế nào về ba cấp độ sâu?
Robin Kanters

5
@Robin sẽ không hiệu quả phải không? this.abbreviation = ((Map<String, Object>)portalCharacteristics.get("icon")).get("ticker").toString();
Marcello de Sales,

cùng một phương pháp có thể được sử dụng để giải nén nhiều giá trị cũng ... unpackValuesFromNestedBrand - nhờ
msanjay

13

Đây là cách tôi xử lý vấn đề này:

Brand lớp học:

package org.answer.entity;

public class Brand {

    private Long id;

    private String name;

    public Brand() {

    }

    //accessors and mutators
}

Product lớp học:

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
}

Ở đây, brandphiên bản sẽ bị bỏ qua bởi Jacksontrong serializationdeserialization, vì nó được chú thích bằng @JsonIgnore.

Jacksonsẽ sử dụng phương thức được chú thích bằng đối tượng @JsonGetterfor serializationof java thành JSONđịnh dạng. Vì vậy, brandNameđược đặt với brand.getName().

Tương tự, Jacksonsẽ sử dụng phương thức được chú thích với @JsonSetterfor deserializationof JSONformat vào đối tượng java. Trong tình huống này, bạn sẽ phải nhanh chóng các brandđối tượng bản thân và thiết lập của nó namesở hữu từ brandName.

Bạn có thể sử dụng @Transientchú thích độ bền brandName, nếu bạn muốn nhà cung cấp độ bền bỏ qua.



1

Tốt nhất là sử dụng các phương pháp setter:

JSON:

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

phương thức setter cho lat hoặc lng sẽ giống như sau:

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

nếu bạn cần đọc cả hai (như bạn thường làm) thì hãy sử dụng phương pháp tùy chỉnh

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

-6

Để làm cho nó đơn giản .. Tôi đã viết mã ... hầu hết nó là tự giải thích.

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
Điều này hoạt động, nhưng tôi đang cố gắng tìm một giải pháp mà tôi không phải tạo một lớp mới cho mọi nút ngay cả khi tôi chỉ cần một trường.
kenske

-7

Xin chào, đây là mã làm việc hoàn chỉnh.

// LỚP KIỂM TRA JUNIT

lớp công cộng {

@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 của 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 

Ý chính đầy đủ: https://gist.github.com/jeorfevre/7c94d4b36a809d4acf2f188f204a8058


1
Phản hồi JSON thực tế mà tôi đang cố gắng lập bản đồ rất phức tạp. Nó có rất nhiều nút và nút con, và việc tạo một lớp cho mỗi nút sẽ rất khó khăn, thậm chí còn hơn thế nữa khi trong hầu hết các trường hợp, tôi chỉ cần một trường duy nhất từ ​​bộ ba nút lồng nhau. Không có cách nào để chỉ lấy một trường duy nhất mà không cần phải tạo hàng loạt lớp mới?
kenske

mở một vé mềm mới và chia sẻ nó với tôi, tôi có thể giúp bạn về điều này. Vui lòng chia sẻ đường dây json mà bạn muốn lập bản đồ. :)
jeorfevre
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.