Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Adding a dynamic json property as java pojo for jackson

Tags:

java

jackson

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?

like image 686
Kishy Nivas Avatar asked Apr 10 '26 05:04

Kishy Nivas


2 Answers

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:

  • Jackson @JsonAnyGetter and @JsonAnySetter Example
  • Jackson Annotation Examples
  • How to use dynamic property names for a Json object
like image 106
Michał Ziober Avatar answered Apr 12 '26 17:04

Michał Ziober


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>

like image 45
matanper Avatar answered Apr 12 '26 19:04

matanper



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!