Rafael
Rafael

Reputation: 6369

Deserialize JSON with Jackson with field dependency

In our project we parse JSON with Jackson. We set field saved by field channelId. Problem is that channelId field is parsed later than saved. So at the time we want to set field saved field channelId is null. How we can set field dependency in JSON deserialization, so field saved will be set after channelId?

This is part of our JSON data:

"message":{  
            "data":{  
               "text":"Some text"
            },
            "saved_by":[  
               2715,
               1234
            ],
            "some_boolean_field":false,
            "channel_id":8162
           }

This is our entity class:

@JsonIgnoreProperties(ignoreUnknown = true)
@org.parceler.Parcel(org.parceler.Parcel.Serialization.BEAN)

public class Message {

    @JsonProperty("channel_id")
    protected long channelId;

    protected boolean saved;

    @JsonSetter("saved_by")
    public void setSavedBy(Set<Long> savedBy) {
        saved = savedBy.contains(getUserIdByChannelId(channelId));
    }

    public long getChannelId() {
        return channelId;
    }

    public void setChannelId(long channelId) {
        this.channelId = channelId;
    }

    public boolean isSaved() {
        return saved;
    }

    public void setSaved(boolean saved) {
        this.saved = saved;
    }

    public void setData(JsonNode data) throws JsonProcessingException {
        JsonNode textNode = data.get("text");
        text = textNode != null ? textNode.asText() : "";

        components = new ArrayList<>();
        JsonNode mediaNode = data.get("media");
        if (mediaNode != null) {
            MessageComponent[] parsedComponents = AppSession.getInstance().getObjectMapper().treeToValue(mediaNode, MessageComponent[].class);
            List<MessageComponent> components = Arrays.asList(parsedComponents).subList(0, parsedComponents.length < 4 ? parsedComponents.length : 4);

            this.components.addAll(components);
        }

        mediaCount = components.size();
    }

    }

Full JSON:

{  
   "data":{  
      "serial":66,
      "updated_entity":"bookmark",
      "bookmark":{  
         "message":{  
            "data":{  
               "text":"hello"
            },
            "counted_serial":748,
            "saved_by":[  
               26526,
               27758
            ],
            "type":"UserMessage",
            "is_reviewed":false,
            "channel_id":8128,
            "id":2841531,
            "replied_message_data":null,
            "is_blocked":false,
            "is_deleted":false,
            "updated_at":"2016-11-21T05:59:52.471Z",
            "spam_reported_by":[  

            ],
            "created_at":"2016-11-19T15:40:17.027Z",
            "uuid":"0b6ba58e-f5e1-4ee5-a9da-041dfc2c85cd",
            "liked_by":[  

            ],
            "user":{  
               "last_name":"M",
               "id":4537,
               "first_name":"John",
               "is_deleted":false,
               "avatar_thumb":"https:\/\/cdn.site.org\/uploads\/99ef4d68-6eaf-4ba6-aafa-74d1cf895d71\/thumb.jpg"
            },
            "serial":934
         },
         "id":6931,
         "created_at":"2016-11-21T05:59:52.459Z",
         "is_deleted":false,
         "updated_at":"2016-11-21T05:59:52.459Z"
      }
   },
   "type":"action_performed"
}

Upvotes: 0

Views: 979

Answers (1)

It's a bit hackish, but by making the Message class its own deserialization-builder, you get a kind of "ready for bean creation"-event in which you have access to all of the properties.

My suggestion is that you try the following :

@JsonDeserialize(builder = Message.class)
public class Message {

    ...

    @JsonSetter("saved_by")
    public void setSavedBy(Set<Long> savedBy) {
        // Merely store the value for later use.
        this.savedBy = savedBy;
    }

    ...

    public Message build() {
        // Calculate value of "saved" field.
        this.saved = this.savedBy.contains(getUserIdByChannelId(this.channelId));
        return this;
    }

    // Handling the added challenge.
    @JsonProperty("data") 
    public void setData(JsonNode data) throws JsonProcessingException {
       ...
    }
}

The above takes advantage of the default settings of the JsonPOJOBuilder annotation, namely that the default value for buildMethodName is build.

Upvotes: 2

Related Questions