3

I need to create a Json payload like this from Java pojo :

{ "update" : {
    "labels" : [{"add" : "A label"} ]
    }
 }

so I created a Java pojo like this :

@JsonRootName(value = "update")
public class AddLabel{
  @JsonProperty("labels")
  public List<Label> labels; 
  public AddLabel(String labelName){
     this.labels = new ArrayList<>(); 
     this.labels.add(new Label(labelName); 
  }
  public static class Label{
    public String add; 
    public Label(String labelName){
       this.add = labelName; 
    }
  }
}

I like to have the "add" property in Label class as dynamic, so that it can also take values such as "remove", "set" . Is there a way to do this other than creating another POJO's for each of them?

Kishy Nivas
  • 663
  • 8
  • 21

2 Answers2

4

You can use JsonAnyGetter annotation which allows to create dynamic key-value pairs. Below example shows how we can generate 3 different keys for the same Label class:

import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonRootName;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

public class JsonApp {

    public static void main(String[] args) throws Exception {
        AddLabel label = new AddLabel("A label");
        label.getLabels().add(AddLabel.Label.remove("Remove"));
        label.getLabels().add(AddLabel.Label.set("Set"));

        ObjectMapper mapper = new ObjectMapper();
        mapper.enable(SerializationFeature.INDENT_OUTPUT);

        System.out.println(mapper.writeValueAsString(label));
    }
}

@JsonRootName(value = "update")
class AddLabel {

    @JsonProperty("labels")
    private List<Label> labels;

    public AddLabel(String labelName) {
        this.labels = new ArrayList<>();
        this.labels.add(Label.add(labelName));
    }

    public List<Label> getLabels() {
        return labels;
    }

    public static class Label {

        private final String key;
        private final String value;

        private Label(String key, String value) {
            this.key = key;
            this.value = value;
        }

        public static Label add(String value) {
            return new Label("add", value);
        }

        public static Label remove(String value) {
            return new Label("remove", value);
        }

        public static Label set(String value) {
            return new Label("set", value);
        }

        @JsonAnyGetter
        public Map<String, String> getDynamic() {
            return Collections.singletonMap(key, value);
        }
    }
}

Above code prints:

{
  "labels" : [ {
    "add" : "A label"
  }, {
    "remove" : "Remove"
  }, {
    "set" : "Set"
  } ]
}

See also:

Michał Ziober
  • 37,175
  • 18
  • 99
  • 146
1

Your specification isn't clear, you would have many of the same action (add, remove, etc) or just one at most?

First Option
In case you have at most single instance of the same action, you can replace the array with regular object:

{ "update" : {
    "labels" : {
       "add" : "A label",
       "remove" : "B label"
      }
    }
 }

and then change your labels to a Map<String, String> instead of List<Label>

Second Option
In case you have many instances of the same action, you can change the design of label to contain two fields, value and action

{ "update" : {
    "labels" : [{"action": "add", "value" : "A label"} ]
    }
 }

And update Label accordingly:

  public static class Label{
    public String action;
    public String value 
    public Label(String action, String value){
       this.action = action;
       this.value = value; 
    }
  }

You can also change action to enum which define all your possible actions

Third Option
If you really need to stick to your json design, you don't have a lot of options to define static typing class for a dynamic json. maybe it's possible to hack this somehow using jackons's polymorphism. But you can always just use the json objects and leave the labels as List<JsonObject>

matanper
  • 881
  • 8
  • 24