476

I am getting the following error when trying to get a JSON request and process it:

org.codehaus.jackson.map.JsonMappingException: No suitable constructor found for type [simple type, class com.myweb.ApplesDO]: can not instantiate from JSON object (need to add/enable type information?)

Here is the JSON I am trying to send:

{
  "applesDO" : [
    {
      "apple" : "Green Apple"
    },
    {
      "apple" : "Red Apple"
    }
  ]
}

In Controller, I have the following method signature:

@RequestMapping("showApples.do")
public String getApples(@RequestBody final AllApplesDO applesRequest){
    // Method Code
}

AllApplesDO is a wrapper of ApplesDO :

public class AllApplesDO {

    private List<ApplesDO> applesDO;

    public List<ApplesDO> getApplesDO() {
        return applesDO;
    }

    public void setApplesDO(List<ApplesDO> applesDO) {
        this.applesDO = applesDO;
    }
}

ApplesDO:

public class ApplesDO {

    private String apple;

    public String getApple() {
        return apple;
    }

    public void setApple(String appl) {
        this.apple = apple;
    }

    public ApplesDO(CustomType custom){
        //constructor Code
    }
}

I think that Jackson is unable to convert JSON into Java objects for subclasses. Please help with the configuration parameters for Jackson to convert JSON into Java Objects. I am using Spring Framework.

EDIT: Included the major bug that is causing this problem in the above sample class - Please look accepted answer for solution.

Joeri Hendrickx
  • 16,947
  • 4
  • 41
  • 53
Lucky Murari
  • 12,672
  • 5
  • 22
  • 43
  • 2
    I don't see any subclasses in the above code, is this code what your trying or are you making up a simpler example? – gkamal Oct 02 '11 at 12:35
  • I added an answer with some more explanation of how it works. Basically, you need to realize Java doesn't keep method argument names in runtime. – Vlasec Aug 09 '16 at 10:16

14 Answers14

608

So, finally I realized what the problem is. It is not a Jackson configuration issue as I doubted.

Actually the problem was in ApplesDO Class:

public class ApplesDO {

    private String apple;

    public String getApple() {
        return apple;
    }

    public void setApple(String apple) {
        this.apple = apple;
    }

    public ApplesDO(CustomType custom) {
        //constructor Code
    }
}

There was a custom constructor defined for the class making it the default constructor. Introducing a dummy constructor has made the error to go away:

public class ApplesDO {

    private String apple;

    public String getApple() {
        return apple;
    }

    public void setApple(String apple) {
        this.apple = apple;
    }

    public ApplesDO(CustomType custom) {
        //constructor Code
    }

    //Introducing the dummy constructor
    public ApplesDO() {
    }

}
SteamFire
  • 367
  • 2
  • 14
Lucky Murari
  • 12,672
  • 5
  • 22
  • 43
  • May I ask where CustomType comes from. I am trying a structure like this but I am absolutely new to Java. – andho Jan 11 '13 at 09:19
  • @andho CustomType is just a random name I gave in this madeup example. The CustomType was defined in one of the other classes – Lucky Murari Jan 27 '13 at 07:41
  • Yeah figured that out hehe. My problem was I was using a nested class, which cannot be used with Jackson. – andho Jan 29 '13 at 00:11
  • 187
    You can use jackson with inner (nested) classes, serialization works just fine in this case. The only stumbling block is that the inner class must be marked as "static" for deserialization to work properly. See exlanation here: http://cowtowncoder.com/blog/archives/2010/08/entry_411.html – jpennell Feb 14 '13 at 01:35
  • 3
    Could someone please explain why this happens? I had a very similar error. Thought all the right constructors were in place, but could not deserialize. It only worked after I added a dummy constructor, after reading this post. – user8658912 Apr 28 '14 at 14:59
  • @user8658912 : here's my understanding - when ElasticSearch serializes / deserializes using a Java class, it looks for the dummy, empty constructor and then the class's get/set methods to actually get/set the values. When the dummy constructor is missing, it gives up and throws an error. – Suman Aug 01 '14 at 15:37
  • 7
    @Suman I would not call this a dummy constructor - it's just the default constructor. Not only is it perfectly valid, but is required for many kinds of java bean-type processing. (And, of course, it tripped me up. :-) ) – fool4jesus Apr 20 '15 at 19:26
  • 2
    If you don't want to add default constructor (e.g., when you are dealing with immutable objects). You will have to tell which constructor or factory method to use to instantiate the object using JsonCreator annotation. – Rahul Mar 14 '16 at 14:21
  • Thanks, i added dummy constructor on my POJO class and error go away and custom constructor works well as i required. – Hiren Jun 16 '16 at 04:29
  • The reason you need a "default no argument constructor" FooClass() is likely because Spring follows the JavaBean Specification, which requires this to work for automatically marshalling and unmarshalling when serializing and deserializing objects. – atom88 Oct 20 '17 at 17:15
  • Problem for me was in trying to use an 'inner' class as the POJO. Moving it to a separate file solved the problem. Shame on me :( – atlas_scoffed Nov 07 '18 at 10:06
393

This happens for these reasons:

  1. your inner class should be defined as static

    private static class Condition {  //jackson specific    
    }
    
  2. It might be that you got no default constructor in your class (UPDATE: This seems not to be the case)

    private static class Condition {
        private Long id;
    
        public Condition() {
        }
    
        // Setters and Getters
    }
    
  3. It could be your Setters are not defined properly or are not visible (e.g. private setter)

Siddharth
  • 9,349
  • 16
  • 86
  • 148
azerafati
  • 18,215
  • 7
  • 67
  • 72
  • 101
    static class made the difference in my case. Thanks! – jalogar Nov 20 '14 at 10:12
  • 3
    And of course, you **don't** need to declare an empty, no-argument default constructor, [Java does it for you](http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.8.9)! (As long as you don't define any other constructors.) – Jonik Feb 04 '15 at 13:21
  • 1
    @Jonik, right! my answer is old, if I remember correct, since Jackson is using reflection to get to the inner class, I guess it was needed to define the default constructor(could be also not a case in newer versions), but since I'm not sure you could be correct. – azerafati Feb 04 '15 at 14:37
  • 6
    Yeah, my experience is with Jackson 2.4.4: indeed Java's implicit default constructor is enough. You need to explicitly write the no-args constructor *only* if you've defined *other* constructors that do take arguments (i.e. when Java does not generate the no-args one for you). – Jonik Feb 10 '15 at 16:57
  • 1
    I logged in so that I can say thank you, making my inner classes static solved the problem – Goran Vasic Oct 27 '15 at 20:49
  • Perfect, i had another constructor for differing reasons and needed the default empty one as well – Joe Maher Feb 13 '16 at 11:02
  • 'static' modifier for inner class is the most ridiculous not so well known requirement. – user482594 Apr 19 '17 at 02:11
  • Yup, static modifier should be there as well as default constructor. – Obaid May 02 '17 at 12:55
  • Having the same problem, but I can't add static modifier as it reports that it's not permitted on the class. – Brian Knoblauch Sep 26 '17 at 14:35
  • 1
    The reason you need a "default no argument constructor" FooClass() is likely because Spring follows the JavaBean Specification, which requires this to work for automatically marshalling and unmarshalling when serializing and deserializing objects. – atom88 Oct 20 '17 at 17:16
59

I would like to add another solution to this that does not require a dummy constructor. Since dummy constructors are a bit messy and subsequently confusing. We can provide a safe constructor and by annotating the constructor arguments we allow jackson to determine the mapping between constructor parameter and field.

so the following will also work. Note the string inside the annotation must match the field name.

import com.fasterxml.jackson.annotation.JsonProperty;
public class ApplesDO {

        private String apple;

        public String getApple() {
            return apple;
        }

        public void setApple(String apple) {
            this.apple = apple;
        }

        public ApplesDO(CustomType custom){
            //constructor Code
        }

        public ApplesDO(@JsonProperty("apple")String apple) {
        }

}
PiersyP
  • 5,003
  • 2
  • 33
  • 35
  • This solution did the trick. I just want to mention the following, I had a constructor, but the name of the parameters was different than that of the instance-parameters, so it couldn't be mapped. Adding the annotation solved it, but renaming the parameters would have probably worked also. – Fico Jul 23 '15 at 07:47
  • What if the constructor parameter is not in the response? Can it be injected another way? – Eddie Jaoude Nov 25 '15 at 04:52
  • The only significant data that is being sent here is the String apple that *is* the response. – PiersyP Nov 25 '15 at 12:25
  • 1
    This was useful to me because I wanted my object to be immutable, so a dummy constructor was not an option. – Jonathan Pullano Apr 07 '16 at 16:38
  • In my case it also requires to add ``@JsonCreator`` on the constructor with ``@JsonProperty``. – m1ld Apr 27 '16 at 12:08
31

When I ran into this problem, it was a result of trying to use an inner class to serve as the DO. Construction of the inner class (silently) required an instance of the enclosing class -- which wasn't available to Jackson.

In this case, moving the inner class to its own .java file fixed the problem.

jmarks
  • 655
  • 6
  • 16
  • 7
    Whereas moving the inner class to its own .java file works, adding the **static** modifier also addresses the problem as mentioned in @bludream's answer. – jmarks Feb 15 '16 at 21:56
25

Generally, this error comes because we don’t make default constructor.
But in my case:
The issue was coming only due to I have made used object class inside parent class.
This has wasted my whole day.

Ahmed Nabil
  • 17,392
  • 11
  • 61
  • 88
alok
  • 2,718
  • 21
  • 17
13

Thumb Rule: Add a default constructor for each class you used as a mapping class. You missed this and issue arise!
Simply add default constructor and it should work.

Badal
  • 4,078
  • 4
  • 28
  • 28
10

Can you please test this structure. If I remember correct you can use it this way:

{
    "applesRequest": {
        "applesDO": [
            {
                "apple": "Green Apple"
            },
            {
                "apple": "Red Apple"
            }
        ]
    }
}

Second, please add default constructor to each class it also might help.

danny.lesnik
  • 18,479
  • 29
  • 135
  • 200
8

You have to create dummy empty constructor in our model class.So while mapping json, it set by setter method.

Suresh Patil
  • 121
  • 1
  • 5
6

Regarding the last publication I had the same problem where using Lombok 1.18.* generated the problem.

My solution was to add @NoArgsConstructor (constructor without parameters), since @Data includes by default @RequiredArgsConstructor (Constructor with parameters).

lombok Documentation https://projectlombok.org/features/all

That would solve the problem:

package example.counter;

import javax.validation.constraints.NotNull;

import lombok.Data;

@Data
@NoArgsConstructor
public class CounterRequest {
    @NotNull
    private final Integer int1;

    @NotNull
    private final Integer int2;
}
5

You must realize what options Jackson has available for deserialization. In Java, method argument names are not present in the compiled code. That's why Jackson can't generally use constructors to create a well-defined object with everything already set.

So, if there is an empty constructor and there are also setters, it uses the empty constructor and setters. If there are no setters, some dark magic (reflections) is used to do it.

If you want to use a constructor with Jackson, you must use the annotations as mentioned by @PiersyP in his answer. You can also use a builder pattern. If you encounter some exceptions, good luck. Error handling in Jackson sucks big time, it's hard to understand that gibberish in error messages.

Yoon5oo
  • 496
  • 5
  • 11
Vlasec
  • 5,500
  • 3
  • 27
  • 30
  • The reason you need a "default no argument constructor" FooClass() is likely because Spring follows the JavaBean Specification, which requires this to work for automatically marshalling and unmarshalling when serializing and deserializing objects. – atom88 Oct 20 '17 at 17:18
  • Well, Java serialization and deserialization into binary stream is not what the question is about, anyway. So it is only good that Jackson offers multiple patterns to be used with deserialization. I especially like the builder pattern as it allows the resulting object to be immutable. – Vlasec Oct 23 '17 at 10:41
5

If you start annotating constructor, you must annotate all fields.

Notice, my Staff.name field is mapped to "ANOTHER_NAME" in JSON string.

     String jsonInString="{\"ANOTHER_NAME\":\"John\",\"age\":\"17\"}";
     ObjectMapper mapper = new ObjectMapper();
     Staff obj = mapper.readValue(jsonInString, Staff.class);
     // print to screen

     public static class Staff {
       public String name;
       public Integer age;
       public Staff() {         
       }        

       //@JsonCreator - don't need this
       public Staff(@JsonProperty("ANOTHER_NAME") String   n,@JsonProperty("age") Integer a) {
        name=n;age=a;
       }        
    }
Vortex
  • 789
  • 12
  • 21
1

Add default constructors to all the entity classes

Akash Yellappa
  • 2,126
  • 28
  • 21
0

Failing custom jackson Serializers/Deserializers could also be the problem. Though it's not your case, it's worth mentioning.

I faced the same exception and that was the case.

Artem Novikov
  • 4,087
  • 1
  • 27
  • 33
0

For me, this used to work, but upgrading libraries caused this issue to appear. Problem was having a class like this:

package example.counter;

import javax.validation.constraints.NotNull;

import lombok.Data;

@Data
public class CounterRequest {
    @NotNull
    private final Integer int1;

    @NotNull
    private final Integer int2;
}

Using lombok:

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.0</version>
</dependency>

Falling back to

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.16.10</version>
</dependency>

Fixed the issue. Not sure why, but wanted to document it for future.

eis
  • 51,991
  • 13
  • 150
  • 199