Jackson-Deserialisierung: Kann ich einen Wert mit einer Anmerkung in das Feld des zu deserialisierbaren Objekts einfügen?
Ich habe ein Objekt wie dieses zum Deserialisieren:
public class RelationsInput {
Relation relation1;
Relation relation2;
}
während die Klasse so Relation
aussieht:
public class Relation {
RelationType relationtype;
... (more fields)
}
RelationType
ist en enum und ist kein Wert, der deserialisiert wird, während alle anderen es sind.
Ist es möglich, dass ich den Aufzählungswert für das Feld relationType
mit einer Anmerkung zum Feld in der Klasse "einfügen" kann RelationInput
? Wie das folgende
public class RelationsInput {
@RelationType(RelationType.OWNER)
Relation owner;
@RelationType(RelationType.TENANT)
Relation tenant;
}
Bietet Jackson so etwas an?
Antworten
Sie können versuchen, einen benutzerdefinierten Deserialisierer mit com.fasterxml.jackson.databind.deser.ContextualDeserializer
Schnittstelle zu implementieren . Es ermöglicht die Erstellung einer Deserialisiererinstanz mit einem Kontext.
Siehe Beispiel unten:
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());
}
}
Oben Code für eine Nutzlast:
{
"owner": {
"id": 1
},
"tenant": {
"id": 2
}
}
Drucke:
RelationsInput(owner=Relation(id=1, relationtype=OWNER), tenant=Relation(id=2, relationtype=TENANT))
Siehe auch:
- Deserialisieren Sie mit Jackson zu String oder Object
- So injizieren Sie Abhängigkeiten in Jackson Custom Deserializer
- Jackson - Deserialisieren Sie die innere Liste der Objekte in eine Liste einer höheren Ebene
Ich fürchte, so etwas gibt es nicht. Wenn Sie eine Annotation wie diese verwenden möchten, können Sie benutzerdefinierte De-Serializer verwenden. Erstelle so etwas wie:
@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;
}
}
Dann implementieren Sie die tatsächlichen:
public class OwnerDeserializer extends RelationTypeDeserializer {
public OwnerDeserializer() {
super(RelationType.OWNER);
}
}
und
public class TenantDeserializer extends RelationTypeDeserializer {
public TenantDeserializer() {
super(RelationType.TENANT);
}
}
dann benutze solche wie:
@Getter @Setter
public class RelationsInput {
@JsonDeserialize(using = OwnerDeserializer.class)
private Relation owner;
@JsonDeserialize(using = TenantDeserializer.class)
private Relation tenant;
}
Wenn Sie das tun, wonach Sie fragen, haben Sie immer den gleichen Wert für das Feld RelationType. Eine mögliche Lösung ist die Verwendung eines personalisierten Serializer-Deserializers wie folgt:
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);
Um die Aufzählung vor und zurück zu konvertieren, empfehle ich die Verwendung von map <String, RelationType>, super einfach und perfekt, ungefähr so:
Map<String, RelationType> map = new HashMap<String, RelationType>();
map.put("Some Type", RelationType.SOME_TYPE);
map.put("Some Other Type", RelationType.SOME_OTHER_TYPE);
Verwenden Sie get (string) zum Serialisieren und get (value) zum Deserialisieren (Suchen Sie den Schlüssel eines Werts). Dies ist ein allgemeines Beispiel, wenn Sie etwas serialisieren-deserialisieren möchten, das keinen Standard-Serializer-Deserializer hat Weitere Informationen Wie analysiere ich die Color Java-Klasse mit Jackson in JSON?