Jackson:如何在序列化过程中对 JsonNode 进行后处理?
Posted
技术标签:
【中文标题】Jackson:如何在序列化过程中对 JsonNode 进行后处理?【英文标题】:Jackson: How do I post-process JsonNode during serialization? 【发布时间】:2020-02-07 05:52:21 【问题描述】:我正在尝试实现HL7 FHIR spec's assertion that JSON representing a FHIR model will not have empty objects nor empty arrays。为了不让我的消费者的生活更加困难,我没有在反序列化期间严格执行这一点,但我想确保我的库生成的序列化 JSON 符合指定的要求。我正在使用 Java 和 Jackson ObjectMapper 将对象序列化为 JSON。我对编写自定义序列化程序的理解是,无论您要转换为什么,Object 在某一时刻都表示为 JsonNode。
我想做的是在 JsonNode 退出序列化程序时拦截它,对其进行一些调整(查找并删除空数组和对象),然后让它继续前进。我需要在无法调整 ObjectMapper 的环境中执行此操作,因为我无权访问它。此外,该库中模型的复杂层次结构大量使用 Jackson 的默认序列化和注释等,我无法消除这一点。
如果我为基本类型定义自定义序列化程序,比如说“资源”,那么我就有问题了,因为我仍然需要原始序列化程序的输出来生成我修改后的输出。此外,这需要适应模型中各种类型可能已经存在的任何自定义序列化程序。
我在使用https://www.baeldung.com/jackson-call-default-serializer-from-custom-serializer 和最后一个选项实现 BeanSerializerModifier 的情况下取得了相当大的进展,但我遇到了无法控制我的库消费者使用的 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"
总结
无论使用何种 ObjectMapper,我如何保留现有的序列化机制,并以某种方式从 Jackson 序列化过程生成的传出 JSON 中删除空列表和对象?
编辑:
我还尝试了@JsonInclude(JsonInclude.Include.NON_EMPTY)
,它确实省略了空列表实现。但是,这个库中的绝大多数数据都是由序列化为映射和图元的 POJO 表示的,并且只有当它们直接由模型中的映射和图元表示时,此注解才有效。
【问题讨论】:
@JsonSerialize(include = Inclusion.NON_EMPTY)
?
@dai JsonSerialize.Inclusion
已弃用,但将其现代等效项 @JsonInclude(JsonInclude.Include.NON_EMPTY)
放在我的模型上仅适用于地图、列表和 srting,不适用于序列化为地图、列表或字符串的对象.它在序列化中过早地执行检查。我将在原始问题中对此进行说明。
这确实揭示了NON_EMPTY 文档中提到的一些内容,这使我得到了这个答案,这可能会使注释起作用:***.com/a/31034525/1708977 不幸的是,这也需要我修改映射器,我无法访问。
【参考方案1】:
解决方案是使用自定义@JsonInclude
,即new in Jackson 2.9。感谢@dai 将我指向此功能。
在基础 Resource 类中,如下所示:
@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 表示 省略 属性,我使用了第二种方法来减少此答案中的混淆。
【讨论】:
以上是关于Jackson:如何在序列化过程中对 JsonNode 进行后处理?的主要内容,如果未能解决你的问题,请参考以下文章