简体   繁体   中英

Excluding NullNode when serializing a Jackson JSON tree model

I have a pojo type that needs to have specific numeric values set to a special string when it's serialized. These values will always be null, possibly pretty deep into a hierarchy.

To accomplish this I first convert the pojo to a JsonNode with nulls intact to preserve property order, then I follow a path through the structure to set some strings and create nodes as necessary. Finally, I have the ObjectMapper serialize the JsonNode to a string. The logic looks something like this:

ObjectMapper nonNullMapper = new ObjectMapper();
nonNullMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);

ObjectMapper includeAllMapper = new ObjectMapper();
includeAllMapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);

// NullNodes create stubs to maintain property order
JsonNode node = includeAllMapper.valueToTree(pojoInstance);
insertStrings(node);

String json = nonNullMapper.writeValueAsString(node); 
// Halp, there's still nulls

Note that I'm aware there's a @JsonInclude annotation so I don't actually need two mappers, but it turns out that I want to serialize the pojo instances without nulls elsewhere, so I can't use it to my knowledge.

Anyway, how can I avoid having the NullNodes serialized into my string of json? I've found two approaches so far:

  1. Convert to a Map and then serialize to a String, with SerializationFeature.WRITE_NULL_MAP_VALUES disabled. This seems inefficient and hacky.
  2. Remove NullNode instances manually before serializing the JsonNode. Seems like it shouldn't be necessary given the support for excluding nulls for pojos and maps, and it adds (perhaps?) unneeded complexity.

I tried registering a JsonSerializer for NullNode , but it doesn't appear to get used. I notice that NullNode itself implements JsonSerializable , which simply delegates to the SerializerProvider 's null value serializer. I hesitate to attempt to overwrite this and I feel like the null filtering should be taking place before the values are serialized, but I didn't dig deep enough to understand exactly how it works.

Is there a better way?

Further to @StaxMan's answer, here's some code to prune the NullNodes:

public static void stripNulls(JsonNode node) {
    Iterator<JsonNode> it = node.iterator();
    while (it.hasNext()) {
        JsonNode child = it.next();
        if (child.isNull())
            it.remove();
        else
            stripNulls(child);
    }
}

I don't know of a good solution, but just thought I point out that JSON Tree Model ( JsonNode ) is used to present exact JSON structure; and as such, very few of general features for filtering or transformation are applied. It is very close to WYSIWYG.

Because of this, I would probably rather just traverse the Tree and prune NullNode s explicitly. Trying to configure Jackson itself to do this ends up being more work than explicit removal of nodes.

I'm guessing that your problem is due to your attempt to get the String value of an already serialized object.

What should the setSerializationInclusion parameters do when you aren't serializing anything (since you already serialized it with a different ObjectMapper )?

You might try to make those fields something special (a very simple POJO that extends Integer) and make custom converters for them that return the desired value when they are null.

See objectMapper.registerModule . You will just need to create a SimpleModule and some JsonSerializer s for the new special object types that you created. They could be very simple:

public class SpecialOverriddenNumberSerializer
  extends JsonSerializer<SpecialOverriddenNumber>
{
  private final static String ILLEGAL_VALUE_TEXT = "Some text";

  @Override
  public void serialize (
    SpecialOverriddenNumber value,
    JsonGenerator jgen,
    SerializerProvider provider )
    throws IOException, JsonProcessingException
  {
    // ILLEGAL_VALUE could be equivalent to Integer.MAX_VALUE or something
    // (possibly even null -- though I'm not sure if that would work well
    // with the NON_NULL serialization setting)
    if(value == SpecialOverriddenNumber.ILLEGAL_VALUE)
    {
      // jgen.writeNull();
      jgen.writeString(ILLEGAL_VALUE_TEXT);
    }
    else
    {
      jgen.writeNumber(value);
    }
  }
}

If you also need a deserializer, you can simply create a JsonDeserializer that returns null if it reads ILLEGAL_VALUE_TEXT .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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