当前位置: 首页 > 知识库问答 >
问题:

Jackson:在序列化过程中如何对JsonNode进行后期处理?

毛宏达
2023-03-14

我试图实现HL7 FHIR规范的断言,即表示FHIR模型的JSON将不会有空对象或空数组。为了不让我的消费者的生活变得更加艰难,我在反序列化过程中没有严格执行这一点,但我想确保我的库生成的序列化JSON符合规定。我正在使用Java和Jackson ObjectMapper将对象序列化为JSON。我对编写自定义序列化程序的理解是,对象在某一点上表示为JsonNode,而不管转换为什么。

我想做的是在JsonNode退出序列化程序时拦截它,对它进行一些调整(查找并删除空数组和对象),然后让它继续运行。我需要在无法调整ObjectMapper的环境中执行此操作,因为我无法访问它。此外,该库中复杂的模型层次结构大量使用Jackson的默认序列化和注释等,我无法消除这一点。

如果我为基类型定义一个自定义序列化程序,比如说“资源”,那么我就有问题了,因为我仍然需要原始序列化程序的输出来生成修改后的输出。此外,这需要容纳模型中各种类型上可能已经存在的任何自定义序列化程序。

我在上面的选项中使用了https://www.baeldung.com/jackson-call-default-serializer-from-custom-serializer和最后一个选项,实现了BeanSerializerModimer,但是我遇到了一个问题,我不能控制我的库消费者使用的ObjectMapper。

示例POJO(使用Lombok作为访问器):

@Data
@JsonInclude(JsonInclude.Include.NON_EMPTY)
@JsonIgnoreProperties(ignoreUnknown = true)
abstract class Resource {
  private FhirString id;
  private List<Extension> extension;

  @JsonProperty(access = JsonProperty.Access.READ_ONLY)
  public abstract ResourceType getResourceType();
}
@Data
@Builder
class SomethingElse extends Resource {
  FhirUri someProperty;
  CodeableConcept someCode;
  List<Reference> someReferences;

  @Override
  public ResourceType getResourceType() {
    return ResourceType.SOMETHING_ELSE;
  }
}

以及SomethingElse类的一个示例:

SomethingElse somethingElse = SomethingElse.builder()
    .someProperty(FhirUri.from("some-simple-uri"))
    .someCode(new CodeableConcept())
    .someReference(List.of(new Reference()))
    .build();
somethingElse.setId(FhirString.randomUuid());
somethingElse.setExtension(new ArrayList<>());

当我告诉任何映射程序(或者,例如,使用Spring服务)将SomethingElse类映射到JsonNode时,例如,我可能会得到空的对象和数组,如下所示:

ObjectMapper mapper = getUntouchableMapper();
JsonNode somethingElseNode = mapper.valueToTree(somethingElse);
System.out.println(somethingElseNode.toString());

变成:

{
  "resourceType": "SomethingElse",
  "id": "00000000-0002-0004-0000-000000000000",
  "someProperty": "some-simple-uri",
  "someCode": {},
  "someReferences": [{}],
  "extension": []
}

根据FHIR,这实际上应该是这样的:

{
  "resourceType": "SomethingElse",
  "id": "00000000-0002-0004-0000-000000000000",
  "someProperty": "some-simple-uri"
}

总结

我如何保留已经到位的序列化机制,不管使用的对象映射器,并以某种方式从杰克逊序列化过程产生的传出JSON中删除空列表和对象?

编辑:我还尝试了@JsonInclude(JsonInclude.Include.NON_EMPTY),它省略了空列表实现。然而,该库中的绝大多数数据由POJO表示,POJO序列化为地图和基本体,只有当它们直接由模型中的地图和基本体表示时,该注释才有效。

共有1个答案

郭琦
2023-03-14

解决方案是使用定制的@JsonInclude,这是Jackson 2.9中新增的。谢谢@dai为我指出这个功能。

在基本资源类上,如下所示:

@JsonInclude(value = JsonInclude.Include.CUSTOM, valueFilter = FhirJsonValueFilter.class)
class Resource implements FhirTypeInterface {
  ...

  @Override
  public boolean isEmpty() {
    //Details omitted for simplicity
  }
}

对于可见性,上面使用的界面:

interface FhirTypeInterface {
  boolean isEmpty();
}

我对FhirJsonValueFilter的自定义定义实现了Json包括。NON_EMPTY的所有功能,但也增加了针对FHIR类型实现的方法进行检查的功能(实现与答案无关)。

public class FhirJsonValueFilter {
    @Override
    public boolean equals(Object value) {
        return !getWillInclude(value);
    }

    /**
     * Returns true for an object that matched filter criteria (will be 
     * included) and false for those to omit from the response.
     */
    public boolean getWillInclude(Object value) {
        //Omit explicit null values
        if (null == value) {
            return false;
        }

        //Omit empty collections
        if (Collection.class.isAssignableFrom(value.getClass())) {
            return !((Collection) value).isEmpty();
        }

        //Omit empty maps
        if (Map.class.isAssignableFrom(value.getClass())) {
            return !((Map) value).isEmpty();
        }

        //Omit empty char sequences (Strings, etc.)
        if (CharSequence.class.isAssignableFrom(value.getClass())) {
            return ((CharSequence) value).length() > 0;
        }

        //Omit empty FHIR data represented by an object
        if (FhirTypeInterface.class.isAssignableFrom(value.getClass())) {
            return !((FhirTypeInterface) value).isEmpty();
        }

        //If we missed something, default to include it
        return true;
    }
}

请注意,自定义省略过滤器使用Java的对象。等于功能性,其中true意味着省略属性,我使用了第二种方法来减少这个答案中的混淆。

 类似资料:
  • 我正在使用jackson构建一个rest服务,其中有一个ObjectMapper实例,我可以在其中设置我的配置。Java的值是pojos,具有String和int等类型的字段。很简单,情况直白,没什么特别的。 我想在反序列化后对给定类型的每个字段执行一些处理,可能会更改应该放在pojo字段中的值。我不想在POJO中乱扔注释或任何东西,它应该在ObjectMapper中是独立的。我也不想覆盖现有的反

  • 来自REST服务的json对象 使用JacksonInFiveMinutes中的代码 ObjectMapper mapper=new ObjectMapper(); Map userData=mapper.read值(webResource.queryParams(queryParams). get(String.class);, Map.class); 在哪里: 从REST服务返回json 从J

  • 我的控制器接收到的JSON数据如下: 问题是对象“ContainedObject”被解释为是,并且它正在被实例化。因此,只要我的控制器读取这个JSON数据,它就会生成一个ContainedObject对象类型的实例,但我需要它为空。 最简单、最快的解决方案是,在接收的JSON数据中,该值为空,如下所示: 我的实体类如下: 和包含的对象类,如下所示:

  • 问题内容: 我整天都在寻找可以解决这个问题的东西,但是到目前为止我还没有碰到很多运气。 我的问题很简单:如何使用Jackson正确反序列化匿名对象。 在出现异常之前,此输出为:JSON = [“ com.foo.test.JacksonTest $ 1”,{“ value”:5}]: 线程“主”中的异常com.fasterxml.jackson.databind.JsonMappingExcept

  • 无法从字符串“1979-12-05T08:00Z”反序列化java.util.Date类型的值:无效表示(错误:无法分析日期值“1979-12-05T08:00Z”:无法分析日期“1979-12-05T08:00.000Z”:虽然它似乎符合格式“yyyy-mm-dd't'hh:mm:ss.sss'z'',但分析失败 到目前为止,我试图包含这种依赖关系: 还有: 但没有奏效。