簡體   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