[英]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.