繁体   English   中英

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

[英]Jackson: How do I post-process JsonNode during serialization?

我正在尝试实现HL7 FHIR 规范的断言,即代表 FHIR model 的 JSON 不会有空对象,也不会有空 arrays 为了不让我的消费者的生活变得更加困难,我在反序列化期间没有严格执行这一点,但我想确保我的库生成的序列化 JSON 符合规定。 我正在使用 Java 和Jackson ObjectMapper 将对象序列化为 JSON。 我对编写自定义序列化程序的理解是,Object 在某一时刻表示为 JsonNode,无论您要转换为什么。

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

如果我 go 为基本类型定义自定义序列化程序的路线,比如说“资源”,那么我就有问题了,因为我仍然需要原始序列化程序的 output 才能生成我修改后的 Z78E6221F6398F1CE63 此外,这需要适应 model 中各种类型可能已经存在的任何自定义序列化程序。

使用https://www.baeldung.com/jackson-call-default-serializer-from-custom-serializer和最后一个选项,实现 BeanSerializerModifier,我在上面的选项中走了很远,但我遇到了我不能的问题t 控制我的库使用者使用的 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 class的示例:

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<>());

When I tell any mapper (or, for example, use a Spring service) to map the SomethingElse class into JsonNode, I can, for example, end up with empty objects and arrays, like this:

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"
}

总结

无论使用何种 ObjectMapper,我如何保留已经存在的序列化机制,并以某种方式从 Jackson 序列化过程产生的传出 JSON 中删除空列表和对象?

编辑:我也尝试@JsonInclude(JsonInclude.Include.NON_EMPTY) ,它确实省略了空列表实现。 然而,这个库中的绝大多数数据是由序列化为映射和图元的 POJO 表示的,并且只有当它们直接由 model 中的映射和图元表示时,此注释才有效。

解决方案是使用自定义@JsonInclude ,这是Jackson 2.9 中的新功能 感谢@dai 将我指向此功能。

在基础资源 class 上,如下所示:

@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 的自定义定义实现了JsonInclude.Include.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 的 Object.equals 功能,其中 true 意味着省略该属性,我使用了第二种方法来减少此答案中的混淆。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM