Jackson JSON deserialization with multiple parameters constructor

75,318

Solution 1

You need to add the annotation @JsonProperty specifying the name of the json property that needs to be passed to the constructor when creating the object.

public class Cruise extends WaterVehicle {

 private Integer maxSpeed;

  @JsonCreator
  public Cruise(@JsonProperty("name") String name, @JsonProperty("maxSpeed")Integer maxSpeed) {
    super(name);
    System.out.println("Cruise.Cruise");
    this.maxSpeed = maxSpeed;
  }

  public Integer getMaxSpeed() {
    return maxSpeed;
  }

  public void setMaxSpeed(Integer maxSpeed) {
    this.maxSpeed = maxSpeed;
  }

}

EDIT

I just tested using the below code and it works for me

import java.io.IOException;

import com.fasterxml.jackson.annotation.JsonCreator.Mode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;

class WaterVehicle {

    private String name;
    private int capacity;
    private String inventor;
    public WaterVehicle(String name) {
        this.name=name;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getCapacity() {
        return capacity;
    }
    public void setCapacity(int capacity) {
        this.capacity = capacity;
    }
    public String getInventor() {
        return inventor;
    }
    public void setInventor(String inventor) {
        this.inventor = inventor;
    }


}

 class Cruise  extends WaterVehicle{

        private Integer maxSpeed;

        public Cruise(String name, Integer maxSpeed) {
            super(name);
            this.maxSpeed = maxSpeed;
        }

        public Integer getMaxSpeed() {
            return maxSpeed;
        }

        public void setMaxSpeed(Integer maxSpeed) {
            this.maxSpeed = maxSpeed;
        }


    }

public class Test {
      public static void main(String[] args) throws IOException {
        Cruise cruise = new Cruise("asd", 100);
        cruise.setMaxSpeed(100);
        cruise.setCapacity(123);
        cruise.setInventor("afoaisf");

        ObjectMapper mapper = new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT);
        mapper.registerModule(new ParameterNamesModule(Mode.PROPERTIES));

        String jsonString = mapper.writeValueAsString( cruise);
        System.out.println(jsonString);

        Cruise anotherCruise = mapper.readValue(jsonString, Cruise.class);
         System.out.println(anotherCruise );
         jsonString = mapper.writeValueAsString( anotherCruise );
         System.out.println(jsonString);

    }

}

It produces the following output

{
  "name" : "asd",
  "capacity" : 123,
  "inventor" : "afoaisf",
  "maxSpeed" : 100
}
Cruise@56f4468b
{
  "name" : "asd",
  "capacity" : 123,
  "inventor" : "afoaisf",
  "maxSpeed" : 100
}

Make sure you have the compilerArgs in the pom file.

<compilerArgs>
     <arg>-parameters</arg>
</compilerArgs>

Solution 2

Short answer: use Java 8, javac -parameters, and jackson-module-parameter-names

Long answer: Why when a constructor is annotated with @JsonCreator, its arguments must be annotated with @JsonProperty?

Solution 3

@JsonCreator is not required after having @JsonProperty("xxx") in the parameter

Share:
75,318
pedrostanaka
Author by

pedrostanaka

I have a major in Computer Science and I love programming. I like to contribute to open source projects as well, and when I have some free time in my master's degree daily routine I do it very proudly. Currently, I am a software engineer @ Legal One GmbH

Updated on September 29, 2021

Comments

  • pedrostanaka
    pedrostanaka over 2 years

    I've been using FasterXML/Jackson-Databind in my project for a while now, and all was working great, until I've discovered this post and started to use this approach to desserialize objects without the @JsonProperty annotations.

    The problem is that when I have a constructor which take multiple parameters and decorate this constructor with the @JsonCreator annotation Jackson throw the following error:

    Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: 
    Argument #0 of constructor [constructor for com.eliti.model.Cruiser, annotations: {interface com.fasterxml.jackson.annotation.JsonCreator=@com.fasterxml.jackson.annotation.JsonCreator(mode=DEFAULT)}] has no property name annotation; must have name when multiple-parameter constructor annotated as Creator
     at [Source: {
      "class" : "com.eliti.model.Cruiser",
      "inventor" : "afoaisf",
      "type" : "MeansTransport",
      "capacity" : 123,
      "maxSpeed" : 100
    }; line: 1, column: 1]
    

    I've created a little project to illustrate the problem, the class I'm trying to desserialize is this one:

    public class Cruise extends WaterVehicle {
    
     private Integer maxSpeed;
    
      @JsonCreator
      public Cruise(String name, Integer maxSpeed) {
        super(name);
        System.out.println("Cruise.Cruise");
        this.maxSpeed = maxSpeed;
      }
    
      public Integer getMaxSpeed() {
        return maxSpeed;
      }
    
      public void setMaxSpeed(Integer maxSpeed) {
        this.maxSpeed = maxSpeed;
      }
    
    }
    

    And the code to desserialize is like this:

    public class Test {
      public static void main(String[] args) throws IOException {
        Cruise cruise = new Cruise("asd", 100);
        cruise.setMaxSpeed(100);
        cruise.setCapacity(123);
        cruise.setInventor("afoaisf");
    
        ObjectMapper mapper = new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT);
        mapper.registerModule(new ParameterNamesModule(JsonCreator.Mode.PROPERTIES));
    
        String cruiseJson = mapper.writeValueAsString(cruise);
    
        System.out.println(cruiseJson);
    
        System.out.println(mapper.readValue(cruiseJson, Cruise.class));
    
    }
    

    I already tried to remove the @JsonCreator, but if I do so, the throws the following exception:

    Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of com.eliti.model.Cruise: no suitable constructor found, can not deserialize from Object value (missing default constructor or creator, or perhaps need to add/enable type information?)
     at [Source: {
      "class" : "com.eliti.model.Cruise",
      "inventor" : "afoaisf",
      "type" : "MeansTransport",
      "capacity" : 123,
      "maxSpeed" : 100
    }; line: 3, column: 3]
    

    I have tried to issue a "mvn clean install", but the problem persists.

    Just to include some extra information, I've researched thoroughly about this problem (GitHub issues, Blog posts, StackOverflow Q&A). Here are some debbuging/investigation that I have been doing on my end:

    Investigation 1

    javap -v on the generated bytecode give me this:

     MethodParameters:
          Name                           Flags
          name
          maxSpeed
    

    When talking about the constructor, so I guess that the -parameters flag is really being set for javac compiler.

    Investigation 2

    If I create a constructor with a single parameter the object gets initialized, but I want/need to use the multiple parameter constructor.

    Investigation 3

    If I use the annotation @JsonProperty on each field it works as well, but for my original project it is too much overhead since I have a lot of fields in the constructor (and also it gets very hard to refactor code with annotations).

    The question that remain is: How can I make Jackson work with multiple parameter constructor without annotations?

  • pedrostanaka
    pedrostanaka over 7 years
    That's the thing, I don't want to use annotations in each constructor parameter, because in my original project there are a lot of parameters in classes. I wanted to use something like this: manosnikolaidis.wordpress.com.
  • pedrostanaka
    pedrostanaka over 7 years
    Thanks for the input, but this solution did not work as well. It only seems to work in classes where there is only one constructor. When you have multiple constructors you have to hint Jackson which one to use to construct your objects.
  • Srini Karthikeyan
    Srini Karthikeyan almost 3 years
    My issue got fixed when I added a single-argument string constructor. @JsonProperty doesn't work though. Not sure what is the reason.