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