Jackson 反序列化:我可以在可反序列化对象的字段上注入带有注释的值吗?
Jackson deserialization: Can I inject a value with an annotation on the field of the to deserializable object?
我有一个这样的对象要反序列化:
public class RelationsInput {
Relation relation1;
Relation relation2;
}
而 class Relation
看起来像这样:
public class Relation {
RelationType relationtype;
... (more fields)
}
RelationType
是一个枚举,不是一个将被反序列化的值,而所有其他值都是。
我是否可以在 class RelationInput
的字段上“注入”带有注释的字段 relationType
的枚举值?
喜欢以下
public class RelationsInput {
@RelationType(RelationType.OWNER)
Relation owner;
@RelationType(RelationType.TENANT)
Relation tenant;
}
Jackson 提供这样的东西吗?
恐怕没有这样的事情。如果你想使用一些注释,你可以使用自定义反序列化器。首先创建类似的东西:
@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;
}
您可以尝试使用 com.fasterxml.jackson.databind.deser.ContextualDeserializer
接口实现自定义解串器。它允许创建带有上下文的反序列化器实例。
见下例:
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))
另请参阅:
- How to inject dependency into Jackson Custom deserializer
如果您按要求行事,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);
为了前后转换枚举,我推荐使用 map,超级简单而且工作完美,像这样:
Map<String, RelationType> map = new HashMap<String, RelationType>();
map.put("Some Type", RelationType.SOME_TYPE);
map.put("Some Other Type", RelationType.SOME_OTHER_TYPE);
并使用get(string)序列化
并 get(value) 反序列化(找到某个值的键)
这是一个通用示例,当您想要序列化-反序列化没有默认序列化器-反序列化器的东西时
查看此以获取更多信息
我有一个这样的对象要反序列化:
public class RelationsInput {
Relation relation1;
Relation relation2;
}
而 class Relation
看起来像这样:
public class Relation {
RelationType relationtype;
... (more fields)
}
RelationType
是一个枚举,不是一个将被反序列化的值,而所有其他值都是。
我是否可以在 class RelationInput
的字段上“注入”带有注释的字段 relationType
的枚举值?
喜欢以下
public class RelationsInput {
@RelationType(RelationType.OWNER)
Relation owner;
@RelationType(RelationType.TENANT)
Relation tenant;
}
Jackson 提供这样的东西吗?
恐怕没有这样的事情。如果你想使用一些注释,你可以使用自定义反序列化器。首先创建类似的东西:
@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;
}
您可以尝试使用 com.fasterxml.jackson.databind.deser.ContextualDeserializer
接口实现自定义解串器。它允许创建带有上下文的反序列化器实例。
见下例:
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))
另请参阅:
- How to inject dependency into Jackson Custom deserializer
如果您按要求行事,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);
为了前后转换枚举,我推荐使用 map
Map<String, RelationType> map = new HashMap<String, RelationType>();
map.put("Some Type", RelationType.SOME_TYPE);
map.put("Some Other Type", RelationType.SOME_OTHER_TYPE);
并使用get(string)序列化
并 get(value) 反序列化(找到某个值的键)
这是一个通用示例,当您想要序列化-反序列化没有默认序列化器-反序列化器的东西时
查看此以获取更多信息