java - 如何使用尊重自定义注释的 jackson 执行自定义 JSON 反序列化?

标签 java json kotlin jackson deserialization

我正在尝试将 JSON 反序列化为我无法修改的自定义 POJO。该 POJO 具有来自我无法使用的不同自定义内部序列化框架的注释。如何创建一个尊重这些注释的自定义反序列化器?
这是一个示例 POJO:

public class ExampleClass {
    @Property(name = "id")
    public String id;

    @Property(name = "time_windows")
    @NotNull
    public List<TimeWindow> timeWindows = new ArrayList<>();

    public static class TimeWindow {
        @Property(name = "start")
        public Long start;

        @Property(name = "end")
        public Long end;
    }
}
所以在这种情况下,反序列化器会在 JSON 中查找与 Property 相对应的字段。注释,并使用该注释中的值来决定要抓取的字段。如果属性没有 Property注释,它应该被忽略。
我一直在浏览 Jackson 文档,但无法准确找到我需要的东西。这是一个AnnotationIntrospector 的地方吗?会有用吗?或者可能是 ContextualDeserializer ?
任何指向正确方向的指针将不胜感激!

更新:我尝试实现评论中的建议,但没有成功。
这是我对自省(introspection)器的初始实现:
class CustomAnnotationInspector : JacksonAnnotationIntrospector () {
    override fun hasIgnoreMarker(m: AnnotatedMember?): Boolean {
        val property = m?.getAnnotation(Property::class.java)
        return property == null
    }

    override fun findNameForDeserialization(a: Annotated?): PropertyName {
        val property = a?.getAnnotation(Property::class.java)
        return if (property == null) {
            super.findNameForDeserialization(a)
        } else {
            PropertyName(property.name)
        }
    }
}
这是我实际使用它的地方:
// Create an empty instance of the request object.
val paramInstance = nonPathParams?.type?.getDeclaredConstructor()?.newInstance()

// Create new object mapper that will write values from
// JSON into the empty object.
val mapper = ObjectMapper()

// Tells the mapper to respect custom annotations.
mapper.setAnnotationIntrospector(CustomAnnotationInspector())

// Write the contents of the request body into the StringWriter
// (this is required for the mapper.writeValue method
val sw = StringWriter()
sw.write(context.bodyAsString)

// Deserialize the contents of the StringWriter
// into the empty POJO.
mapper.writeValue(sw, paramInstance)
不幸的是,findNameForDeserialization永远不会被调用,并且没有任何 JSON 值被写入 paramInstance .谁能发现我哪里出错了?
谢谢!

更新 2:我稍微更改了代码,我现在能够识别属性名称,但 jackson 未能创建对象的实例。
这是我的新代码:
val mapper = ObjectMapper()

// Tells the mapper to respect CoreNg annotations.
val introspector = CustomAnnotationInspector()
mapper.setAnnotationIntrospector(introspector)

val paramInstance = mapper.readValue(context.bodyAsString,nonPathParams?.type)
我在自定义注释内省(introspection)中的断点被击中。但我得到以下异常:
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `app.employee.api.employee.BOUpsertEmployeeRequest` (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
这是我试图反序列化的 POJO:
public class BOUpsertEmployeeRequest {
    public BOUpsertEmployeeRequest () { }

    @NotNull
    @Property(name = "xref_code")
    public String xrefCode;

    @Property(name = "first_name")
    public String firstName;

    @Property(name = "last_name")
    public String lastName;

    @Property(name = "email_address")
    public String emailAddress;

    @Property(name = "phone")
    public String phone;

    @Property(name = "address")
    public List<String> address;

    @Property(name = "employment_status")
    public String employmentStatus;

    @Property(name = "pay_type")
    public String payType;

    @Property(name = "position")
    public String position;

    @Property(name = "skills")
    public List<String> skills;

    @Property(name = "gender")
    public String gender;
}
据我所知,它有一个默认构造函数。有人知道问题是什么吗?
谢谢!

最佳答案

方法hasIgnoreMarker不仅为字段调用,还为构造函数调用,包括虚拟的:

Method called to check whether given property is marked to be ignored. This is used to determine whether to ignore properties, on per-property basis, usually combining annotations from multiple accessors (getters, setters, fields, constructor parameters).


在这种情况下,您应该只忽略未正确标记的字段:
static class CustomAnnotationIntrospector extends JacksonAnnotationIntrospector {
    @Override
    public PropertyName findNameForDeserialization(Annotated a) {
        Property property = a.getAnnotation(Property.class);
        if (property == null) {
            return PropertyName.USE_DEFAULT;
        } else {
            return PropertyName.construct(property.name());
        }
    }

    @Override
    public boolean hasIgnoreMarker(AnnotatedMember m) {
        return m instanceof AnnotatedField
                && m.getAnnotation(Property.class) == null;
    }
}

示例:
class Pojo {
//    @Property(name = "id")
    Integer id;
//    @Property(name = "number")
    Integer number;
    @Property(name = "assure")
    Boolean assure;
    @Property(name = "person")
    Map<String, String> person;
}
String json =
        "{\"id\" : 1, \"number\" : 12345, \"assure\" : true," +
        " \"person\" : {\"name\" : \"John\", \"age\" : 23}}";

ObjectMapper mapper = new ObjectMapper();
mapper.setAnnotationIntrospector(new CustomAnnotationIntrospector());
Pojo pojo = mapper.readValue(json, Pojo.class);

System.out.println(pojo);
Pojo{id=null, number=null, assure=true, person={name=John, age=23}}

注:定制 Property注释应该有 RetentionPolicy.RUNTIME (与 JsonProperty 注释相同):
@Retention(RetentionPolicy.RUNTIME)
public @interface Property {
    String name();
}

关于java - 如何使用尊重自定义注释的 jackson 执行自定义 JSON 反序列化?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63362602/

相关文章:

java - 如何根据结果内部对象对 mongo 聚合查询进行排序

java - 如何制作一个操纵另一个程序 ui 的程序

ios - JSON 抛出错误

json - Elasticsearch不返回jsonp

java - Android BindingConversion 在 kotlin 中不起作用?

java - XSD 两个元素之间的选择

Java数组随机存储对象

json - Firefox 书签标签如何链接到书签?

firebase - Firestore 在失去并重新获得互联网连接后停止更新

java - 错误 : Class kotlin. reflect.jvm.internal.FunctionCaller$FieldSetter