जैक्सन डिसेरिएलाइजेशन: क्या मैं डेसिरिसेबल ऑब्जेक्ट के क्षेत्र पर एनोटेशन के साथ एक मूल्य इंजेक्ट कर सकता हूं?

Nov 24 2020

मेरे पास इस तरह की वस्तु है जो deserialize करें:

public class RelationsInput {

   Relation relation1;

   Relation relation2;

}

जबकि वर्ग Relationइस तरह दिखता है:

public class Relation {

   RelationType relationtype;
   ... (more fields)

}

RelationType enum है और एक ऐसा मूल्य नहीं है, जिसे deserialized किया जाएगा, जबकि अन्य सभी हैं।

क्या यह संभव है, कि मैं relationTypeकक्षा में क्षेत्र पर एक एनोटेशन के साथ क्षेत्र के लिए एनम मूल्य को "इंजेक्ट" कर सकता हूं RelationInput? निम्नलिखित की तरह


public class RelationsInput {

   @RelationType(RelationType.OWNER)
   Relation owner;

   @RelationType(RelationType.TENANT)
   Relation tenant;

}

क्या जैक्सन ऐसा कुछ प्रदान करता है?

जवाब

1 MichałZiober Nov 25 2020 at 00:04

आप com.fasterxml.jackson.databind.deser.ContextualDeserializerइंटरफ़ेस के साथ कस्टम deserialiser लागू करने का प्रयास कर सकते हैं । यह एक संदर्भ के साथ deserialiser उदाहरण बनाने की अनुमति देता है।

नीचे देखें उदाहरण:

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.fasterxml.jackson.databind.json.JsonMapper;
import lombok.Data;

import java.io.File;
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

public class JsonContextualDeserializerApp {
    public static void main(String[] args) throws IOException {
        File jsonFile = new File("./resource/test.json").getAbsoluteFile();

        ObjectMapper mapper = JsonMapper.builder().build();
        RelationsInput info = mapper.readValue(jsonFile, RelationsInput.class);

        System.out.println(info.toString());
    }
}

@Data
class RelationsInput {

    @JsonDeserialize(using = RelationStdDeserializer.class)
    @RelationTypeInfo(RelationType.OWNER)
    private Relation owner;

    @JsonDeserialize(using = RelationStdDeserializer.class)
    @RelationTypeInfo(RelationType.TENANT)
    private Relation tenant;

}

@Data
class Relation {

    private int id;
    private RelationType relationtype;
}

enum RelationType {OWNER, TENANT}

@Retention(RetentionPolicy.RUNTIME)
@interface RelationTypeInfo {
    RelationType value();
}

class RelationStdDeserializer extends StdDeserializer<Relation> implements ContextualDeserializer {

    private RelationType propertyRelationType;

    public RelationStdDeserializer() {
        this(null);
    }

    public RelationStdDeserializer(RelationType relationType) {
        super(Relation.class);
        this.propertyRelationType = relationType;
    }

    @Override
    public Relation deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        JsonDeserializer<Object> deser = ctxt.findRootValueDeserializer(ctxt.getTypeFactory().constructType(Relation.class));
        Relation instance = (Relation) deser.deserialize(p, ctxt);
        if (this.propertyRelationType != null) {
            instance.setRelationtype(this.propertyRelationType);
        }
        return instance;
    }

    @Override
    public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) {
        RelationTypeInfo typeInfo = property.getMember().getAllAnnotations().get(RelationTypeInfo.class);

        return new RelationStdDeserializer(typeInfo.value());
    }
}

एक पेलोड के लिए उपरोक्त कोड:

{
  "owner": {
    "id": 1
  },
  "tenant": {
    "id": 2
  }
}

प्रिंट:

RelationsInput(owner=Relation(id=1, relationtype=OWNER), tenant=Relation(id=2, relationtype=TENANT))

यह सभी देखें:

  • जैक्सन का उपयोग करते हुए स्ट्रिंग या ऑब्जेक्ट के लिए Deserialize
  • जैक्सन कस्टम deserializer में निर्भरता इंजेक्षन करने के लिए कैसे
  • जैक्सन - एक उच्च स्तर की सूची के लिए वस्तुओं की आंतरिक सूची का deserialize
pirho Nov 24 2020 at 21:48

मुझे डर है कि ऐसी कोई बात नहीं है। यदि आप कुछ एनोटेशन का उपयोग करना चाहते हैं जैसे कि आप कस्टम डे-सीरियलाइज़र का उपयोग कर सकते हैं। पहले कुछ बनाएं जैसे:

@RequiredArgsConstructor
public abstract class RelationTypeDeserializer extends JsonDeserializer<Relation> {
    private final RelationType relationType;

    @Override
    public Relation deserialize(JsonParser p, DeserializationContext ctxt)
            throws IOException, JsonProcessingException {
        Relation r = p.readValueAs(Relation.class);
        r.setRelationtype(relationType);
        return r;
    }
}

फिर वास्तविक लोगों को लागू करें:

public class OwnerDeserializer extends RelationTypeDeserializer {
    public OwnerDeserializer() {
        super(RelationType.OWNER);
    }   
}

तथा

public class TenantDeserializer extends RelationTypeDeserializer {
    public TenantDeserializer() {
        super(RelationType.TENANT);
    }
}

तो उन का उपयोग करें:

@Getter @Setter
public class RelationsInput {
    @JsonDeserialize(using = OwnerDeserializer.class)
    private Relation owner;
    @JsonDeserialize(using = TenantDeserializer.class)
    private Relation tenant;
}
JesusHernandezBarrios Nov 25 2020 at 00:24

यदि आप वह करते हैं जो आप मांगते हैं, तो आपके पास हमेशा RelationType फ़ील्ड के लिए समान मान होगा। वैसे भी, एक पॉसिबल सॉल्यूशन इस तरह से वैयक्तिकृत सीरियल-डिसेरिएलाइज़र का उपयोग कर रहा है:

public class RelationTypeJsonSerializer extends JsonSerializer<RelationType> {

    @Override
    public void serialize(RelationType value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        String string = value.toString();//or something like that, convert the enum to string as you like
        gen.writeString(string);
    }
}

public class RelationTypeJsonDeserializer extends JsonDeserializer<RelationType> {

    @Override
    public RelationType deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        String toString = p.getCodec().readValue(p, String.class);//read the value as string
        return RelationType.build(toString);//convert back the value to object, use switch if needed)
    }
}

ObjectMapper om = new ObjectMapper();
SimpleModule localDateModule = new SimpleModule("RelationType Module");
localDateModule.addSerializer(RelationType.class, new RelationTypeJsonSerializer());
localDateModule.addDeserializer(RelationType.class, new RelationTypeJsonDeserializer());
om.registerModule(localDateModule);

एनम को वापस बदलने और आगे बढ़ाने के लिए मैं मानचित्र का उपयोग फिर से करता हूं <String, RelationType>, सुपर सरल और सही काम, कुछ इस तरह:

Map<String, RelationType> map = new HashMap<String, RelationType>();
map.put("Some Type", RelationType.SOME_TYPE);
map.put("Some Other Type", RelationType.SOME_OTHER_TYPE);

और क्रमबद्ध करने के लिए और (मान) पाने के लिए (स्ट्रिंग) का उपयोग करें (कुछ मूल्य की कुंजी खोजें) यह एक सामान्य उदाहरण है जब आप कुछ को क्रमबद्ध करना चाहते हैं-जो किसी डिफ़ॉल्ट सीरियल-डिसेरिएलाइज़ को नहीं देखना चाहता है। अधिक जानकारी के लिए मैं जैक्सन के साथ जैसन के लिए कलर जावा क्लास कैसे पार्स करूं?