当前位置: 首页 > 面试题库 >

如何使用Jackson在注释的基础上对对象的字段进行反序列化?

祁烈
2023-03-14
问题内容

我需要以一种特定的方式配置Jackson,以下将对其进行描述。

  1. 带注释的字段仅使用其ID进行序列化:
    • 如果该字段是普通对象,请对其序列化 id
    • 如果该字段是对象的集合,请序列化一个数组 id
  2. 带注释的字段的属性名称以不同的顺序进行序列化:
    • 如果该字段是普通对象,请"_id"在属性名称后添加后缀
    • 如果该字段是对象的集合,请"_ids"在属性名称后添加后缀
  3. 对于注释,我想这样的自定义@JsonId,最好有一个可选的值来覆盖名字就像@JsonProperty
  4. 用户应使用以下方法定义id属性:
    • 已经存在的杰克逊 @JsonIdentityInfo
    • 或通过创建另一个类或字段注释
    • 或通过确定要检查id属性发现性的注释(例如,对于JPA场景很有用)
  5. 对象应该使用包装的根值序列化
  6. 驼峰式命名应转换为带下划线的小写
  7. 所有这些都应该可反序列化(通过构造一个仅设置了id的实例)

一个例子

考虑以下POJO:

//Inform Jackson which property is the id
@JsonIdentityInfo(
    generator = ObjectIdGenerators.PropertyGenerator.class,
    property = "id"
)
public abstract class BaseResource{
    protected Long id;

    //getters and setters
}

public class Resource extends BaseResource{
    private String name;
    @JsonId
    private SubResource subResource;
    @JsonId
    private List<SubResource> subResources;

    //getters and setters
}

public class SubResource extends BaseResource{
    private String value;

    //getters and setters
}

Resource实例的可能序列化可能是:

{
    "resource":{
        "id": 1,
        "name": "bla",
        "sub_resource_id": 2,
        "sub_resource_ids": [
            1,
            2,
            3
        ]
    }
}

至今…

  • 可以通过以下方式配置来实现要求 #5ObjectMapper
    objectMapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);
    

    objectMapper.configure(SerializationFeature.WRAP_ROOT_VALUE, true);

然后@JsonRootName("example_root_name_here")在我的POJO中使用。

  • 可以通过以下方式配置来实现要求 #6ObjectMapper
    objectMapper.setPropertyNamingStrategy(
    PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES);
    

如您所见,仍然有很多要求可以满足。对于那些想知道为什么需要这样的配置的人,是因为我正在为ember.js(更具体地说是Ember
Data)开发REST Web服务。如果您能满足任何要求,将不胜感激。

谢谢!


问题答案:

您的大部分(全部?)要求都可以通过使用上下文序列化程序来实现。通过ContextualDeserializer的一个答案,使用Jackson和Jackson的Wiki(http://wiki.fasterxml.com/JacksonFeatureContextualHandlers)将JSON映射到不同类型的地图,我能够提出以下内容。

您需要从@JsonId批注开始,这是指示属性仅需要使用Id属性的键。

import com.fasterxml.jackson.annotation.*;
import java.lang.annotation.*;

@Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation // important so that it will get included!
public @interface JsonId {
}

接下来是实际的ContextualSerializer,它完成了繁重的工作。

import com.fasterxml.jackson.databind.ser.*;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.core.*;
import java.io.*;

public class ContextualJsonIdSerializer
    extends JsonSerializer<BaseResource>
    implements ContextualSerializer/*<BaseResource>*/
{
    private ObjectMapper mapper;
    private boolean useJsonId;

    public ContextualJsonIdSerializer(ObjectMapper mapper) { this(mapper, false); }
    public ContextualJsonIdSerializer(ObjectMapper mapper, boolean useJsonId) {
        this.mapper = mapper;
        this.useJsonId = useJsonId;
    }

    @Override
    public void serialize(BaseResource br, JsonGenerator jgen, SerializerProvider provider) throws IOException
    {
        if ( useJsonId ) {
            jgen.writeString(br.getId().toString());
        } else {
            mapper.writeValue(jgen, br);
        }
    }

    @Override
    public JsonSerializer<BaseResource> createContextual(SerializerProvider config, BeanProperty property)
            throws JsonMappingException
    {
        // First find annotation used for getter or field:
        System.out.println("Finding annotations for "+property);

        if ( null == property ) {
            return new ContextualJsonIdSerializer(mapper, false);
        }

        JsonId ann = property.getAnnotation(JsonId.class);
        if (ann == null) { // but if missing, default one from class
            ann = property.getContextAnnotation(JsonId.class);
        }
        if (ann == null ) {//|| ann.length() == 0) {
            return this;//new ContextualJsonIdSerializer(false);
        }
        return new ContextualJsonIdSerializer(mapper, true);
    }
}

此类查看BaseResource属性并检查它们以查看是否@JsonId存在注释。如果是,则仅使用Id属性,否则将使用传入ObjectMapper来序列化该值。这很重要,因为如果您尝试使用(基本上)在上下文中的映射器,ContextualSerializer则将导致堆栈溢出,因为它最终将一遍又一遍地调用这些方法。

您的资源看起来应该如下所示。我使用@JsonProperty注解而不是在中包装功能,ContextualSerializer因为重新发明轮子似乎很愚蠢。

import java.util.*;
import com.fasterxml.jackson.annotation.*;

public class Resource extends BaseResource{
    private String name;

    @JsonProperty("sub_resource_id")
    @JsonId
    private SubResource subResource;

    @JsonProperty("sub_resource_ids")
    @JsonId
    private List<SubResource> subResources;

    //getters and setters
    public String getName() {return name;}
    public void setName(String name) {this.name = name;}

    public SubResource getSubResource() {return subResource;}
    public void setSubResource(SubResource subResource) {this.subResource = subResource;}

    public List<SubResource> getSubResources() {return subResources;}
    public void setSubResources(List<SubResource> subResources) {this.subResources = subResources;}
}

最后,执行序列化的方法只是创建一个附加对象,ObjectMapper并在原始对象中注册一个模块ObjectMapper

// Create the original ObjectMapper
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);
objectMapper.configure(SerializationFeature.WRAP_ROOT_VALUE, true);
objectMapper.setPropertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES);

// Create a clone of the original ObjectMapper
ObjectMapper objectMapper2 = new ObjectMapper();
objectMapper2.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);
objectMapper2.configure(SerializationFeature.WRAP_ROOT_VALUE, true);
objectMapper2.setPropertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES);

// Create a module that references the Contextual Serializer
SimpleModule module = new SimpleModule("JsonId", new Version(1, 0, 0, null));
// All references to SubResource should be run through this serializer
module.addSerializer(SubResource.class, new ContextualJsonIdSerializer(objectMapper2));
objectMapper.registerModule(module);

// Now just use the original objectMapper to serialize


 类似资料:
  • 问题内容: 我正在杰克逊1.6.2中使用REST Web服务/ ApacheWink。我如何注释枚举字段,以便Jackson对其进行反序列化? 内部阶层 Jackson的文档指出可以通过@JsonValue/来做到这一点,@JsonCreator但没有提供示例。 是否有人愿意像往常一样洒(java)bean? 问题答案: 如果您使用的是Jackson 1.9,则可以通过以下方式完成序列化: 因此,

  • 在实体的自定义反序列化器中,我希望以一种尊重@JSONFORMAT注释的方式递归调用(部分)字段的反序列化。更具体地说,这就是反序列化器的样子: 在该示例中,jsonParser.readValueAs()不接受带注释的 是否有任何方法可以反序列化对象的字段,使其尊重注释格式?

  • 我有一个类,它可以很好地序列化和反序列化: 我还有一个< code>PacketResponse对象,类似于: 对象的序列化工作正常,结果如下: 然而,当我试图将这个JSON反序列化回一个< code>PacketResponse对象时,使用 我得到一个。 似乎它正在尝试反序列化 ,但由于它没有直接执行此操作,杰克逊看不到 注释,而是正在寻找 字段(因为这是构建器方法的名称)。 有人能帮我找出反序

  • 我目前正在开发一个Java web应用程序,它使用Magento REST API公开的JSON数据。api返回的数据示例如下: 我的应用程序中有一个Java类,如下所示: 我想对数据进行反序列化,并将其转换为,但我总是得到以下错误: 这是我用来将JSON响应反序列化为ArrayList的语句行: 有人能分享一些见解吗?我看到一些例子,返回的JSON对象前面没有任何ID。那是因为我做错了什么吗?非

  • 问题内容: 在杰克逊的数据绑定的文档表明,杰克逊支持deserialising“所有支持类型的数组”,但我不能找出确切的语法这一点。 对于单个对象,我可以这样做: 现在,对于数组,我想这样做: 有人知道是否有不可思议的命令吗?如果没有,那么解决方案是什么? 问题答案: 首先创建一个映射器: 作为数组: 作为列表: 指定列表类型的另一种方法: