जैक्सन एनोटेशन का उपयोग करके एक संपत्ति के लिए एक नेस्टेड मूल्य कैसे मैप करें?


90

मान लें कि मैं किसी API के लिए एक कॉल कर रहा हूं जो एक उत्पाद के लिए निम्नलिखित JSON के साथ प्रतिक्रिया करता है:

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

मैं जैक्सन एनोटेशन का उपयोग करके उत्पाद आईडी और नाम को ठीक करने में सक्षम हूं:

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 का उपयोग करके समाप्त कर दिया - वसंत हुड के तहत इसका उपयोग करता है। उदाहरण के लिए, उनके org.springframework.data.web में।
dehumanizer

जवाबों:


89

आप इसे इस तरह हासिल कर सकते हैं:

String brandName;

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

23
लगभग तीन स्तर गहरे कैसे?
रॉबिन कैंटर

5
@ रोबिन वह काम नहीं करेगा? this.abbreviation = ((Map<String, Object>)portalCharacteristics.get("icon")).get("ticker").toString();
मार्सेल्लो डे सेल्स

एक ही विधि कई मूल्यों के रूप में अच्छी तरह से unpacking के लिए इस्तेमाल किया जा सकता है ... 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दौरान serializationऔर deserializationबाद में इसे अनदेखा कर दिया जाएगा @JsonIgnore

Jacksonप्रारूप में जावा ऑब्जेक्ट के @JsonGetterलिए एनोटेट विधि का उपयोग करेगा । तो, के साथ सेट किया गया है ।serializationJSONbrandNamebrand.getName()

इसी तरह, Jacksonविधि के साथ एनोटेट का उपयोग करेगा @JsonSetterके लिए deserializationकी JSONजावा वस्तु में प्रारूप। इस परिदृश्य में, आपको brandऑब्जेक्ट को स्वयं इंस्टेंट करना होगा और उसकी nameप्रॉपर्टी से सेट करना होगा brandName

आप @Transientदृढ़ता एनोटेशन का उपयोग कर सकते हैं brandName, यदि आप चाहते हैं कि इसे दृढ़ता प्रदाता द्वारा अनदेखा किया जाए।


5

आप नेस्टेड संपत्तियों को मैप करने के लिए JsonPath-अभिव्यक्तियों का उपयोग कर सकते हैं। मुझे नहीं लगता कि कोई आधिकारिक समर्थन है ( इस मुद्दे को देखें ), लेकिन यहां एक अनौपचारिक कार्यान्वयन है: https://github.com/elasticpath/json-unmarshaller


1

सेटर विधियों का उपयोग करना सबसे अच्छा है:

JSON:

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

लैट या लैंग के लिए सेटर विधि इस तरह दिखाई देगी:

 @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
यह काम करता है, लेकिन मैं एक समाधान खोजने की कोशिश कर रहा हूं, जहां मुझे हर नोड के लिए एक नया वर्ग बनाने की आवश्यकता नहीं है, भले ही मुझे केवल एक क्षेत्र की आवश्यकता हो।
१६:१६

-7

नमस्ते यहाँ पूरा काम कोड है।

// जूनियर टेस्ट क्लास

पब्लिक क्लास सोफ {

@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 कनिष्ठ टेस्टकेस की

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 प्रतिक्रिया मैं मानचित्र के लिए कोशिश कर रहा हूँ बहुत जटिल है। इसमें बहुत सारे नोड्स और सब्नोड्स हैं, और प्रत्येक के लिए एक वर्ग बनाना बहुत दर्दनाक होगा, और भी अधिक तब जब ज्यादातर मामलों में मुझे केवल ट्रिपल नेस्टेड नोड्स के सेट से एक ही क्षेत्र की आवश्यकता होती है। क्या नई कक्षाओं का निर्माण किए बिना सिर्फ एक क्षेत्र प्राप्त करने का कोई तरीका नहीं है?
14

एक नया सोफ टिकट खोलें और इसे मेरे साथ साझा करें, मैं इस पर आपकी मदद कर सकता हूं। कृपया जिस मैप को आप चाहते हैं, उसे साझा करें। :)
जेफोरिवे
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.