Home > Software design >  Confused about how Jackson JSON property names gets Serialized
Confused about how Jackson JSON property names gets Serialized

Time:11-03

I read about how Jackson assumes that Java Objects follow JavaBeans convention, and that JSON property names will be based on getter/setter methods (ie if theres a getName, it would look for a name property in JSON string, setName would write the name class field into a JSON string). Why does it not get decided by class variable name?

I looked at the Baeldung tutorials and tried looking for documentation on why or how Jackson works, but none of them explains why. It only shows how to use the annotations or how to resolve specific cases where you might want to read List, HashMap, ignore fields etc.

The tutorial did explain how to make fields serializable/deserializable using getter and setter methods, but when working with Java objects that do not follow JavaBeans convention, how do I determine what gets written into the JSON string? I used the following annotations to read a .json file containing an array of books:

 import com.fasterxml.jackson.annotation.JsonAlias;
    import com.fasterxml.jackson.annotation.JsonCreator;
    import com.fasterxml.jackson.annotation.JsonGetter;
    import com.fasterxml.jackson.annotation.JsonProperty;
    import com.fasterxml.jackson.annotation.JsonSetter;
    
    public class Book implements Comparable<Book>{
        private String title;
        private String author;
        //match a field in json string to pojo when names dont match
        @JsonSetter("isbn-10") 
    //  @JsonProperty("isbn")
    //  @JsonAlias("isbn-10")
        private String isbn;
        
        @JsonCreator
        public Book(@JsonProperty("title") String title,@JsonProperty("author") 
                                String author,@JsonProperty("isbn") String isbn) {
            this.title = title;
            this.author = author;
            this.isbn = isbn;
        }
    
        public String getTitle() {
            return title;
        }
    
        public String getAuthor() {
            return author;
        }
    
        @JsonGetter("isbn")
        public String getIsbn() {
            return isbn;
        }
        
        public int compareTo(Book book) {
            return this.getTitle().compareTo(book.getTitle());
        }
    }

sample json file contents:

[
{
        "title":"Day Knight",
        "author":"Pun R. Good",
        "isbn-10":"830456394-2"
    }
]

However if I do not specify the JsonGetter annotation with isbn, i get an error that:

java.lang.IllegalStateException: Conflicting/ambiguous property name definitions (implicit name 'isbn'): found multiple explicit names: [isbn-10, isbn], but also implicit accessor: [method com.fdmgroup.jacksonexercise.Book#getIsbn()][visible=true,ignore=false,explicitName=false]

But if I used the commented out JsonAlias and JsonProperties instead of getter and setter annotations, this issue does not happen at all. Why is it forcing me to specify the annotation of getter when the getter is a regular getter that follows convention and not a weird getter name like getTheIsbn().

Why does it not read isbn-10 into the class field isbn using the JsonSetter, and write out the property based on the variable name along with the value (using JsonGetter or JsonProperties if there is a need to further adjust the name)?

CodePudding user response:

However if I do not specify the JsonGetter annotation with isbn, i get an error that:

You've mixed everything together, there's a lot of redundancy.

First of all since you've defined a creator by annotating the constructor with @JsonCreator Jackson would no longer be interested in setters (they are needed only a no-args constructor is used). Hence, you can get read of @JsonSetter.

Because you need to specify multiple aliases of isbn property, @JsonAlias is the right way to go. These names would be considered only during deserialization from JSON and would have no impact on serialization.

@JsonAlias({"isbn", "isbn-10"})

And since you're happy with the field name isbn in your POJO, which Jackson would infer from the getter-name getIsbn(), using @JsonGetter("isbn") is redundant. It would be useful if you would need to change it, e.g. @JsonGetter("ISBN")*, or @JsonGetter("isbn-10") (method name getIsbn-10() would be illegal in Java).

That said, your class might look like this:

public class Book implements Comparable<Book> {
    private String title;
    private String author;

    @JsonAlias({"isbn", "isbn-10"})
    private String isbn;
    
    @JsonCreator
    public Book(@JsonProperty("title") String title,
                @JsonProperty("author") String author,
                @JsonProperty("isbn-10") String isbn) {
        this.title = title;
        this.author = author;
        this.isbn = isbn;
    }
    
    public String getTitle() {
        return title;
    }
    
    public String getAuthor() {
        return author;
    }
    
    public String getIsbn() {
        return isbn;
    }
    
    public int compareTo(Book book) {
        return this.getTitle().compareTo(book.getTitle());
    }
}
  • Related