मैं जैक्सन का उपयोग करके ऑब्जेक्ट में कच्चे JSON कैसे शामिल कर सकता हूं?


102

मैं एक जावा ऑब्जेक्ट के अंदर कच्चे जेन्सन को शामिल करने की कोशिश कर रहा हूं जब ऑब्जेक्ट जैक्सन का उपयोग करके अनुक्रमित (डी) है। इस कार्यक्षमता का परीक्षण करने के लिए, मैंने निम्नलिखित परीक्षण लिखा:

public static class Pojo {
    public String foo;

    @JsonRawValue
    public String bar;
}

@Test
public void test() throws JsonGenerationException, JsonMappingException, IOException {

    String foo = "one";
    String bar = "{\"A\":false}";

    Pojo pojo = new Pojo();
    pojo.foo = foo;
    pojo.bar = bar;

    String json = "{\"foo\":\"" + foo + "\",\"bar\":" + bar + "}";

    ObjectMapper objectMapper = new ObjectMapper();
    String output = objectMapper.writeValueAsString(pojo);
    System.out.println(output);
    assertEquals(json, output);

    Pojo deserialized = objectMapper.readValue(output, Pojo.class);
    assertEquals(foo, deserialized.foo);
    assertEquals(bar, deserialized.bar);
}

कोड निम्न पंक्ति आउटपुट करता है:

{"foo":"one","bar":{"A":false}}

JSON बिल्कुल है कि मैं चीजों को कैसे देखना चाहता हूं। दुर्भाग्यवश, JSON को ऑब्जेक्ट पर वापस पढ़ने का प्रयास करते समय कोड एक अपवाद के साथ विफल हो जाता है। यहाँ अपवाद है:

org.codehaus.jackson.map.JsonMappingException: java.lang की आवृत्ति का वर्णन नहीं कर सकता। पंक्ति: 1, कॉलम: 13] (संदर्भ श्रृंखला के माध्यम से: com.tnal.prism.cobalt.gather.testing.Pojo ["बार"])

जैक्सन एक दिशा में क्यों ठीक काम करता है लेकिन दूसरी दिशा में जाने पर असफल हो जाता है? ऐसा लगता है कि इसे इनपुट के रूप में फिर से अपना आउटपुट लेने में सक्षम होना चाहिए। मुझे पता है कि मैं जो करने की कोशिश कर रहा हूं वह अपरंपरागत है (सामान्य सलाह यह है barकि उसके पास एक आंतरिक वस्तु है जिसके नाम एक संपत्ति है A), लेकिन मैं इस JSON के साथ बिल्कुल भी बातचीत नहीं करना चाहता। मेरा कोड इस कोड के लिए एक पास-थ्रू के रूप में काम कर रहा है - मैं इस JSON में लेना चाहता हूं और किसी चीज़ को छूने के बिना इसे फिर से वापस भेजना चाहता हूं, क्योंकि जब JSON बदलता है तो मैं नहीं चाहता कि मेरे कोड में संशोधनों की आवश्यकता हो।

सलाह के लिए धन्यवाद।

संपादित करें: पूजो को एक स्थिर वर्ग बनाया, जो एक अलग त्रुटि पैदा कर रहा था।

जवाबों:


64

@JsonRawValue केवल सीरियल-साइड के लिए अभिप्रेत है, क्योंकि रिवर्स दिशा को संभालने के लिए थोड़ा मुश्किल है। वास्तव में इसे पूर्व-एन्कोडेड सामग्री को इंजेक्ट करने की अनुमति देने के लिए जोड़ा गया था।

मुझे लगता है कि रिवर्स के लिए समर्थन जोड़ना संभव होगा, हालांकि यह काफी अजीब होगा: सामग्री को पार्स करना होगा, और फिर "कच्चे" रूप में फिर से लिखना होगा, जो समान हो सकता है या नहीं हो सकता है (जब से चरित्र उद्धृत किया गया है) अलग हो सकता है)। यह सामान्य मामले के लिए। लेकिन शायद यह समस्याओं के कुछ सबसेट के लिए समझ में आता है।

लेकिन मुझे लगता है कि आपके विशिष्ट मामले के लिए एक काम के आसपास 'java.lang.Object' के रूप में टाइप करना होगा, क्योंकि यह ठीक काम करना चाहिए: क्रमांकन के लिए, स्ट्रिंग आउटपुट के रूप में होगा, और deserialization के लिए, इसे deserialized किया जाएगा। एक नक्शा। वास्तव में यदि आप अलग से गेट्टर / सेटर करना चाहते हैं तो ऐसा कर सकते हैं; गेट्टर क्रमांकन के लिए स्ट्रिंग लौटेगा (और @JsonRawValue की जरूरत है); और सेटर या तो मैप या ऑब्जेक्ट ले जाएगा। अगर यह समझ में आता है तो आप इसे एक स्ट्रिंग को फिर से एनकोड कर सकते हैं।


1
यह एक आकर्षण की तरह काम करता है; कोड के लिए मेरी प्रतिक्रिया देखें ( टिप्पणियों में स्वरूपण अजीब है )।
यवेस एम्सलेम

मेरे पास इसके लिए एक अलग उपयोग का मामला था। ऐसा लगता है कि अगर हम डेसर / सेर में बहुत सारे स्ट्रिंग कचरा उत्पन्न नहीं करना चाहते हैं, तो हमें इस तरह से एक स्ट्रिंग को बस में सक्षम करना चाहिए। मैंने एक धागा देखा जो इसे ट्रैक करता है, लेकिन ऐसा लगता है कि कोई मूल समर्थन संभव नहीं है। Markmail.org/message/…
सिड

@ ऐसा करने का कोई तरीका नहीं है और दोनों ही कुशलता से टोकन। अनप्रोसेस्ड टोकन के पास-थ्रू समर्थन के लिए अतिरिक्त स्टेट-कीपिंग की आवश्यकता होती है, जो कुछ हद तक कम कुशल "नियमित" बनाता है। यह नियमित कोड और अपवाद फेंकने के बीच अनुकूलन की तरह है: बाद का समर्थन करने के लिए पूर्व के लिए ओवरहेड जोड़ता है। जैक्सन को अप्रमाणित इनपुट उपलब्ध रखने की कोशिश करने के लिए डिज़ाइन नहीं किया गया है; इसे (त्रुटि संदेशों के लिए भी) बहुत अच्छा होगा, लेकिन इसके लिए अलग दृष्टिकोण की आवश्यकता होगी।
21

55

@StaxMan उत्तर के बाद , मैंने निम्नलिखित कार्यों को एक आकर्षण की तरह बनाया है:

public class Pojo {
  Object json;

  @JsonRawValue
  public String getJson() {
    // default raw value: null or "[]"
    return json == null ? null : json.toString();
  }

  public void setJson(JsonNode node) {
    this.json = node;
  }
}

और, प्रारंभिक प्रश्न के लिए वफादार होने के लिए, यहां काम कर रहा परीक्षण है:

public class PojoTest {
  ObjectMapper mapper = new ObjectMapper();

  @Test
  public void test() throws IOException {
    Pojo pojo = new Pojo("{\"foo\":18}");

    String output = mapper.writeValueAsString(pojo);
    assertThat(output).isEqualTo("{\"json\":{\"foo\":18}}");

    Pojo deserialized = mapper.readValue(output, Pojo.class);
    assertThat(deserialized.json.toString()).isEqualTo("{\"foo\":18}");
    // deserialized.json == {"foo":18}
  }
}

1
मैंने कोशिश नहीं की, लेकिन यह काम करना चाहिए: 1) ऑब्जेक्ट Json 2 के बजाय एक JsonNode नोड बनाते हैं) toString () के बजाय नोड .asText () का उपयोग करें। मैं हालांकि 2 के बारे में निश्चित नहीं हूं।
वादिम किरिलचुक

मुझे आश्चर्य है कि आखिर क्यों getJson()लौटता है String। अगर यह बस सेट्टर के JsonNodeमाध्यम से सेट किया गया था, तो इसे वांछित के रूप में क्रमबद्ध किया जाएगा, नहीं?
सॉरीमिसजैकसन

@VadimKirilchuk node.asText()खाली मूल्य विपरीत देता है toString()
v.ladynev

36

मैं एक कस्टम deserializer (कटौती और यहाँ से चिपकाया ) के साथ ऐसा करने में सक्षम था

package etc;

import java.io.IOException;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.TreeNode;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;

/**
 * Keeps json value as json, does not try to deserialize it
 * @author roytruelove
 *
 */
public class KeepAsJsonDeserialzier extends JsonDeserializer<String> {

    @Override
    public String deserialize(JsonParser jp, DeserializationContext ctxt)
            throws IOException, JsonProcessingException {

        TreeNode tree = jp.getCodec().readTree(jp);
        return tree.toString();
    }
}

6
कितना सरल है। IMO यह आधिकारिक उत्तर होना चाहिए। मैंने बहुत जटिल संरचना के साथ कोशिश की जिसमें एरे, सबजेक्ट, आदि शामिल हैं। हो सकता है कि आप अपना उत्तर संपादित करें और जोड़ दें कि स्ट्रिंग सदस्य को deserialized किया जाना चाहिए @JsonDeserialize (का उपयोग करके = KeepAsJsonDeserialzier.class)। (और अपनी कक्षा के नाम में टाइपो को सही करें ;-)
हिरो

यह Deserializion के लिए काम करता है। कच्चे जूस के सीरियल को पूजो में बदलने के बारे में कैसे? वह कैसे पूरा होगा
xtrakBandit

4
@xtrakBandit Serialization के लिए, उपयोग@JsonRawValue
smartwjw

यह एक आकर्षण की तरह काम करता है। धन्यवाद रॉय और @ हेरी .. इस पोस्ट की एक साथ हेरी की टिप्पणी imho सबसे अच्छा जवाब है।
मीकल

सरल और साफ समाधान। मैं @Heri
mahesh nanayakkara

18

@JsonSetter मदद कर सकता है। मेरा नमूना देखें ('डेटा' में अप्रकाशित JSON शामिल है):

class Purchase
{
    String data;

    @JsonProperty("signature")
    String signature;

    @JsonSetter("data")
    void setData(JsonNode data)
    {
        this.data = data.toString();
    }
}

3
JsonNode.toString () प्रलेखन विधि के अनुसार जो नोड के डेवलपर-पठनीय प्रतिनिधित्व का उत्पादन करेगा; जो <b> हो सकता है या नहीं </ b> वैध JSON हो सकता है। तो यह वास्तव में बहुत जोखिम भरा कार्यान्वयन है।
पायोत्र

@Padr अब javadoc का कहना है कि "विधि (जो जैक्सन 2.10 का उत्पादन करेगी) मान्य JSON डेटाटाइंड की डिफ़ॉल्ट सेटिंग्स का उपयोग करते हुए, स्ट्रिंग के रूप में"
bernie

4

रॉय Truelove के महान जवाब में जोड़ने , यह कैसे की उपस्थिति के जवाब में कस्टम deserialiser इंजेक्षन करने के लिए है @JsonRawValue:

import com.fasterxml.jackson.databind.Module;

@Component
public class ModuleImpl extends Module {

    @Override
    public void setupModule(SetupContext context) {
        context.addBeanDeserializerModifier(new BeanDeserializerModifierImpl());
    }
}

import java.util.Iterator;

import com.fasterxml.jackson.annotation.JsonRawValue;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.DeserializationConfig;
import com.fasterxml.jackson.databind.deser.BeanDeserializerBuilder;
import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier;
import com.fasterxml.jackson.databind.deser.SettableBeanProperty;

public class BeanDeserializerModifierImpl extends BeanDeserializerModifier {
    @Override
    public BeanDeserializerBuilder updateBuilder(DeserializationConfig config, BeanDescription beanDesc, BeanDeserializerBuilder builder) {
        Iterator<SettableBeanProperty> it = builder.getProperties();
        while (it.hasNext()) {
            SettableBeanProperty p = it.next();
            if (p.getAnnotation(JsonRawValue.class) != null) {
                builder.addOrReplaceProperty(p.withValueDeserializer(KeepAsJsonDeserialzier.INSTANCE), true);
            }
        }
        return builder;
    }
}

यह जैक्सन 2.9 में काम नहीं करता है। ऐसा लगता है कि यह टूट गया था क्योंकि यह अब संपत्ति संपत्ति में पुरानी संपत्ति का उपयोग करता है। एक के स्थान पर निर्माण करें
dant3

3

यह आपके आंतरिक वर्गों के साथ एक समस्या है। Pojoवर्ग एक है गैर स्थिर भीतरी वर्ग अपने परीक्षण वर्ग के, और जैक्सन नहीं इन्स्तांत उस वर्ग कर सकते हैं। तो यह क्रमबद्ध हो सकता है, लेकिन deserialize नहीं।

इस तरह अपनी कक्षा को फिर से परिभाषित करें:

public static class Pojo {
    public String foo;

    @JsonRawValue
    public String bar;
}

के जोड़ पर ध्यान दें static


धन्यवाद। मुझे एक कदम और आगे बढ़ा, लेकिन अब मुझे एक अलग त्रुटि मिल रही है। मैं नई त्रुटि के साथ मूल पोस्ट को अपडेट करूंगा।
भिल्लोम्रोम

3

इस आसान समाधान ने मेरे लिए काम किया:

public class MyObject {
    private Object rawJsonValue;

    public Object getRawJsonValue() {
        return rawJsonValue;
    }

    public void setRawJsonValue(Object rawJsonValue) {
        this.rawJsonValue = rawJsonValue;
    }
}

इसलिए मैं कच्चे JSONValue चर में JSON के कच्चे मूल्य को संग्रहीत करने में सक्षम था और फिर इसे JSON में अन्य क्षेत्रों के साथ इसे (ऑब्जेक्ट के रूप में) डीरिएरलाइज़ करने और अपने REST के माध्यम से भेजने में कोई समस्या नहीं थी। @JsonRawValue का उपयोग करने से मुझे मदद नहीं मिली क्योंकि संग्रहीत JSON को स्ट्रिंग के रूप में deserialized किया गया था, वस्तु के रूप में नहीं, और यह वह नहीं था जो मैं चाहता था।


3

यह एक JPA इकाई में भी काम करता है:

private String json;

@JsonRawValue
public String getJson() {
    return json;
}

public void setJson(final String json) {
    this.json = json;
}

@JsonProperty(value = "json")
public void setJsonRaw(JsonNode jsonNode) {
    // this leads to non-standard json, see discussion: 
    // setJson(jsonNode.toString());

    StringWriter stringWriter = new StringWriter();
    ObjectMapper objectMapper = new ObjectMapper();
    JsonGenerator generator = 
      new JsonFactory(objectMapper).createGenerator(stringWriter);
    generator.writeTree(n);
    setJson(stringWriter.toString());
}

आदर्श रूप से ObjectMapper और यहां तक ​​कि JsonFactory संदर्भ से हैं और कॉन्फ़िगर किए गए हैं ताकि आपके JSON को सही ढंग से संभाल सकें (मानक या गैर-मानक मान जैसे 'Infinity' तैरता है उदाहरण के लिए)।


1
JsonNode.toString()प्रलेखन के अनुसार Method that will produce developer-readable representation of the node; which may <b>or may not</b> be as valid JSON.तो यह वास्तव में बहुत जोखिम भरा कार्यान्वयन है।
पिओर

हाय @Piotr, संकेत के लिए धन्यवाद। आप निश्चित रूप से सही हैं, यह JsonNode.asText()आंतरिक रूप से उपयोग करता है और इन्फिनिटी और अन्य गैर-मानक JSON मूल्यों का उत्पादन करेगा।
जॉर्ज

@Padr अब javadoc का कहना है कि "विधि (जो जैक्सन 2.10 का उत्पादन करेगी) मान्य JSON डेटाटाइंड की डिफ़ॉल्ट सेटिंग्स का उपयोग करते हुए, स्ट्रिंग के रूप में"
bernie

2

जैक्सन मॉड्यूल का उपयोग करने के @JsonRawValueतरीके को काम करने के तरीके (क्रमांकन और विचलन) दोनों का एक पूर्ण उदाहरण दिया गया है :

public class JsonRawValueDeserializerModule extends SimpleModule {

    public JsonRawValueDeserializerModule() {
        setDeserializerModifier(new JsonRawValueDeserializerModifier());
    }

    private static class JsonRawValueDeserializerModifier extends BeanDeserializerModifier {
        @Override
        public BeanDeserializerBuilder updateBuilder(DeserializationConfig config, BeanDescription beanDesc, BeanDeserializerBuilder builder) {
            builder.getProperties().forEachRemaining(property -> {
                if (property.getAnnotation(JsonRawValue.class) != null) {
                    builder.addOrReplaceProperty(property.withValueDeserializer(JsonRawValueDeserializer.INSTANCE), true);
                }
            });
            return builder;
        }
    }

    private static class JsonRawValueDeserializer extends JsonDeserializer<String> {
        private static final JsonDeserializer<String> INSTANCE = new JsonRawValueDeserializer();

        @Override
        public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
            return p.readValueAsTree().toString();
        }
    }
}

तब आप मॉड्यूल बनाने के बाद रजिस्टर कर सकते हैं ObjectMapper:

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JsonRawValueDeserializerModule());

String json = "{\"foo\":\"one\",\"bar\":{\"A\":false}}";
Pojo deserialized = objectMapper.readValue(json, Pojo.class);

क्या उपरोक्त के अलावा कुछ और है जो आपको करना है? मैंने पाया है कि JsonRawValueDeserializer की deserialize विधि ObjectMapper द्वारा कभी नहीं बुलाई जाती है
माइकल कॉक्सॉन

@MichaelCoxon क्या आपने इसे काम करने के लिए प्रबंधित किया है? एक बात जो मुझे अतीत में परेशान करती थी, वह थी org.codehaus.jacksonपैकेज के एनोटेशन का उपयोग किए बिना इसे साकार करने के लिए। सुनिश्चित करें कि आपके सभी आयात से आते हैं com.fasterxml.jackson
हेलर परेरा

1

मेरे पास एक ही मुद्दा था। मुझे इस पोस्ट में समाधान मिला: जैक्सन या इसके विकल्पों का उपयोग करके सादे वर्ग के लिए पार्स जोंस का पेड़

अंतिम उत्तर देखें। प्रॉपर्टी के लिए एक कस्टम सेटर को परिभाषित करके जो कि JsonNode को पैरामीटर के रूप में लेता है और String प्रॉपर्टी को सेट करने के लिए jsonNode परString विधि को कॉल करता है, यह सभी काम करता है।


1

किसी ऑब्जेक्ट का उपयोग करना दोनों तरीकों से ठीक काम करता है ... इस पद्धति का दो बार में कच्चे मूल्य से अधिक ओवरहेड होना है।

ObjectMapper mapper = new ObjectMapper();
RawJsonValue value = new RawJsonValue();
value.setRawValue(new RawHello(){{this.data = "universe...";}});
String json = mapper.writeValueAsString(value);
System.out.println(json);
RawJsonValue result = mapper.readValue(json, RawJsonValue.class);
json = mapper.writeValueAsString(result.getRawValue());
System.out.println(json);
RawHello hello = mapper.readValue(json, RawHello.class);
System.out.println(hello.data);

RawHello.java

public class RawHello {

    public String data;
}

RawJsonValue.java

public class RawJsonValue {

    private Object rawValue;

    public Object getRawValue() {
        return rawValue;
    }

    public void setRawValue(Object value) {
        this.rawValue = value;
    }
}

1

मुझे एक समान समस्या थी, लेकिन बहुत सारे JSON इटेंस ( List<String>) के साथ एक सूची का उपयोग करना ।

public class Errors {
    private Integer status;
    private List<String> jsons;
}

मैंने @JsonRawValueएनोटेशन का उपयोग करके क्रमांकन को प्रबंधित किया । लेकिन deserialization के लिए मुझे रॉय के सुझाव के आधार पर एक कस्टम deserializer बनाना पड़ा ।

public class Errors {

    private Integer status;

    @JsonRawValue
    @JsonDeserialize(using = JsonListPassThroughDeserialzier.class)
    private List<String> jsons;

}

नीचे आप मेरी "सूची" विवरणिका देख सकते हैं।

public class JsonListPassThroughDeserializer extends JsonDeserializer<List<String>> {

    @Override
    public List<String> deserialize(JsonParser jp, DeserializationContext cxt) throws IOException, JsonProcessingException {
        if (jp.getCurrentToken() == JsonToken.START_ARRAY) {
            final List<String> list = new ArrayList<>();
            while (jp.nextToken() != JsonToken.END_ARRAY) {
                list.add(jp.getCodec().readTree(jp).toString());
            }
            return list;
        }
        throw cxt.instantiationException(List.class, "Expected Json list");
    }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.