简体   繁体   中英

Can I tell Jackson to conditionally serialize a private instance variable based on differently-typed method?

To the best of my understanding, Jackson will

  • serialize a public instance variable to the variable name

    public List<String> myStrings = new ArrayList<>();

    serializes to

    { 'myStrings' : [ ... ] }

  • serialize a private instance variable to the variable name if it has a public getter named getVariable() :

     private List<String> myStrings = new ArrayList<>(); public List<String> getMyStrings() { return myStrings; } 

    serializes similar to

    { 'myStrings' : [ ... ] }


However, what I am trying to achieve is to serialize it to a String (instead of array of Strings) based on another method, but keep the JSON key (based on @JsonInclude(JsonInclude.Include.NON_NULL) suppressing the original accessor in some cases

    @JsonInclude(JsonInclude.Include.NON_NULL)

    private boolean firstStringOnly = true;
    private List<String> myStrings = new ArrayList<>();
    public List<String> getMyStrings() { return firstStringOnly ? null: myStrings; }
    public String getFirstString() { return firstStringOnly ? myStrings.get(0) : null; } 

Desired JSON serialization:

  • For firstStringOnly==true : { 'myStrings' : 'first_String' } (using getFirstString() )

  • For firstStringOnly==false : { 'myStrings' : [ ... ] } (using getMyStrings() )

Is this possible to do? I'm specifically looking to avoid using custom serializers, and do this via annotations only.

You can assume a reasonably recent version of Jackson and Java 8.

Just to re-iterate, the question constraints are:

* NO custom serializer

* Both use cases produce the same JSON key

EDIT: Sorry, i have misread your initial question. I assume you want to keep both of the typed getters. Would this work for you?

public class TestClass {

  private boolean firstStringOnly = true;
  private List<String> myStrings = new ArrayList<>();

  @JsonIgnore
  public boolean isFirstStringOnly() {
    return firstStringOnly;
  }

  public void setFirstStringOnly(boolean firstStringOnly) {
    this.firstStringOnly = firstStringOnly;
  }

  @JsonIgnore
  public List<String> getMyStrings() {
    return firstStringOnly ? null : myStrings;
  }

  @JsonIgnore
  public String getFirstString() { return firstStringOnly ? myStrings.get(0) : null; }

  @JsonProperty("myStrings")
  public Object getMyStringsForSerialization() {
    return firstStringOnly ? getFirstString() : getMyStrings();
  }

  public void setMyStrings(List<String> myStrings) {
    this.myStrings = myStrings;
  }

You can generalize getMyStrings() method and make it return Object. And inside check the flag and return first value or all values. Here is my sample

public class tst {
private static class YourObject {

    private boolean firstStringOnly;
    private List<String> myStrings = new ArrayList<>();

    public YourObject(boolean firstStringOnly) {
        this.firstStringOnly = firstStringOnly;
        this.myStrings.add("str1");
        this.myStrings.add("str2");
    }
    public Object getMyStrings(){
        return firstStringOnly ? myStrings.get(0) : myStrings;
    }

}

    public static void main(String[] args) throws IOException {
        ObjectMapper mapper = new ObjectMapper();
        System.out.println(mapper.writeValueAsString(new YourObject(true)));
        System.out.println(mapper.writeValueAsString(new YourObject(false)));
    }
}

The output is

{"myStrings":"str1"}
{"myStrings":["str1","str2"]}

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