125

I'm running into a json parsing issue when using the ObjectMapper class from the com.fasterxml.jackson.databind package, and the error that I'm getting is:

com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of com.graybar.utilities.ups.beans.Address: no String-argument constructor/factory method to deserialize from String value ('')

The web application where this problem is occurring is a Spring MVC application using an AngularJS front end, but I can duplicate the issue with a much smaller, all java program. Here are my beans:

Shipment.java

@JsonIgnoreProperties(ignoreUnknown = true)
public class Shipment {
    @JsonProperty("Activity")
    private ArrayList<Activity> activity;
    public ArrayList<Activity> getActivity() {
        return activity;
    }
    public void setActivity(ArrayList<Activity> activity) {
        this.activity = activity;
    }
}

Activity.java

@JsonIgnoreProperties(ignoreUnknown = true)
public class Activity {
    @JsonProperty("ActivityLocation")
    private ArrayList<ActivityLocation> activityLocation;
    public ArrayList<ActivityLocation> getActivityLocation() {
        return activityLocation;
    }
    public void setActivityLocation(ArrayList<ActivityLocation> activityLocation) {
        this.activityLocation = activityLocation;
    }
}

ActivityLocation.java

@JsonIgnoreProperties(ignoreUnknown = true)
public class ActivityLocation {
    @JsonProperty("Address")
    private Address address;
    public Address getAddress() {
        return address;
    }
    public void setAddress(Address address) {
        this.address = address;
    }
}

Address.java

@JsonIgnoreProperties(ignoreUnknown = true)
public class Address {
    @JsonProperty("City")
    private String city;
    @JsonProperty("StateProvinceCode")
    private String stateProvinceCode;
    @JsonProperty("CountryCode")
    private String countryCode;
    public String getCity() {
        return city;
    }
    public void setCity(String city) {
        this.city = city;
    }
    public String getCountryCode() {
        return countryCode;
    }
    public void setCountryCode(String countryCode) {
        this.countryCode = countryCode;
    }
    public String getStateProvinceCode() {
        return stateProvinceCode;
    }
    public void setStateProvinceCode(String stateProvinceCode) {
        this.stateProvinceCode = stateProvinceCode;
    }
}

Here is the code where I can properly map the json:

public static void main(String[] args) {
    String jsonMessage = "" +
        "{" + 
        "   \"Activity\": [{ " +
        "       \"ActivityLocation\": { " +
        "           \"Address\": { " +
        "               \"City\": \"Hana\", " +
        "               \"StateProvinceCode\": \"Hi\", " +
        "               \"CountryCode\": \"US\" " +
        "           } " +
        "       } " +
        "   }, " +
        "   { " +
        "       \"ActivityLocation\": { " +
        "           \"Address\": { " +
        "               \"City\": \"Honolulu\", " +
        "               \"StateProvinceCode\": \"Hi\", " +
        "               \"CountryCode\": \"US\" " +
        "           } " +
        "       } " +
        "   }] " +
    "} ";

    try {
        ObjectMapper mapper = new ObjectMapper();
        mapper.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);

        Shipment shipment = mapper.readValue(jsonMessage, Shipment.class);
        System.out.println("shipment.toString = " + shipment.toString());
    } catch (Exception e) {
        e.printStackTrace();
    }
}

When adjusting the data in the jsonMessage var is when I run into the error that I mentioned above:

    "{" + 
    "   \"Activity\": [{ " +
    "       \"ActivityLocation\": { " +
    "           \"Address\": { " +
    "               \"City\": \"Hana\", " +
    "               \"StateProvinceCode\": \"Hi\", " +
    "               \"CountryCode\": \"US\" " +
    "           } " +
    "       } " +
    "   }, " +
    "   { " +
    "       \"ActivityLocation\": { " +
    "           \"Address\": \"\" " +
    "           } " +
    "       } " +
    "   }] " +
    "} ";

So, the problem happens when changing the json from this:

{
    "ActivityLocation": { 
        "Address": {
            "City": "Honolulu", 
            "StateProvinceCode": "Hi", 
            "CountryCode": "US"
        }
    }
}]

to this:

{
"ActivityLocation": {
     "Address": ""
    }
}

Instead of sending values for my Address bean, I'm getting just an empty string. Unfortunately, I'm receiving my data from a third party and have no control over the data I receive.

Is there an annotation that needs to be added to be able to handle this?

Gemmy
  • 21
  • 3
Stephen
  • 1,977
  • 2
  • 15
  • 19

12 Answers12

169

Had this when I accidentally was calling

mapper.convertValue(...)

instead of

mapper.readValue(...)

So, just make sure you call correct method, since argument are same and IDE can find many things

Andrii Plotnikov
  • 3,022
  • 4
  • 17
  • 37
  • 2
    @AbhijitSarkar what? The problem is error "no String-argument constructor/factory method to deserialize.....". The SO has to offer answers not only to TS specific question but to the problem as whole. I've had this problem with slightly different input, but was going through same process of debugging this exact problem. I've posted my answer since it might help with debugging of this particular problem but slightly modified circumstances. Second point is that people will search for problem with this input and it might help them. Input is the same, circumstances are different – Andrii Plotnikov Jun 20 '19 at 09:43
  • For anybody curious why this is the case, it's buried in the docs: https://fasterxml.github.io/jackson-databind/javadoc/2.8/com/fasterxml/jackson/databind/ObjectMapper.html#convertValue(java.lang.Object,%20java.lang.Class) - "Further note that functianality is not designed to support "advanced" use cases, such as conversion of polymorphic values, or cases where Object Identity is used." – louhow Mar 05 '21 at 21:26
64

Try setting

mapper.configure(
          DeserializationConfig.Feature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT,
          true)

or

mapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);

depending on your Jackson version.

gawi
  • 2,843
  • 4
  • 29
  • 44
Abhijit Sarkar
  • 21,927
  • 20
  • 110
  • 219
  • 7
    what would be the reason for this exception being thrown if the json I'm parsing does not have any blank or null values? I'm just trying to get a better understanding all-together. – Kervvv Jul 01 '18 at 02:18
  • @Kervvv this exception won’t be thrown if the input doesn’t have any empty string. – Abhijit Sarkar Jul 01 '18 at 02:47
14

This exception says that you are trying to deserialize the object "Address" from string "\"\"" instead of an object description like "{…}". The deserializer can't find a constructor of Address with String argument. You have to replace "" by {} to avoid this error.

papaRomik
  • 141
  • 1
  • 3
5

Use below code snippet This worked for me

ObjectMapper objectMapper = new ObjectMapper();
String jsonString = "{\"symbol\":\"ABCD\}";
objectMapper.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true);
Trade trade = objectMapper.readValue(jsonString, new TypeReference<Symbol>() {});

Model Class

@JsonIgnoreProperties    public class Symbol {
    @JsonProperty("symbol")
    private String symbol;
}
Suraj Rao
  • 29,388
  • 11
  • 94
  • 103
Abhilash Ranjan
  • 715
  • 8
  • 8
4

I received the same exception because I accidentally quoted an object like

{ "foo": "{ \"value\": 42}" }

instead of

{ "foo": { "value": 42 } }
Matthias
  • 12,053
  • 4
  • 49
  • 91
2

Create Object Mapper as ->

private static final ObjectMapper objectMapper = new ObjectMapper()
        .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
        .configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true);

and then use

objectMapper.convertValue(object, value.class);
CodingBee
  • 1,011
  • 11
  • 8
1

Well, I encountered the same error and it took a long time to debug it. The JSON I was passing had this structure

{
 "details": "id"
}

And the error trace was No String-argument constructor/factory method to deserialize from String value ('id')

Whereas the pojo expected the id to be an object instead, so correct way would be

{
  "details": {
    "key": "id"
  }
}
Saurav
  • 614
  • 7
  • 13
1

I also faced this issue and solution which worked for me is:

String jsonString="";
ObjectMapper objectMapper = new ObjectMapper()
        .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
        .configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true);
JsonNode jsonObj=objectMapper.readTree(jsonString);
TypeObject typeObject=objectMapper.convertValue(jsonObj, TypeObject.class);
0

I found a different way to handle this error. (the variables is according to the original question)

   JsonNode parsedNodes = mapper.readValue(jsonMessage , JsonNode.class);
        Response response = xmlMapper.enable(ACCEPT_EMPTY_STRING_AS_NULL_OBJECT,ACCEPT_SINGLE_VALUE_AS_ARRAY )
                .disable(FAIL_ON_UNKNOWN_PROPERTIES, FAIL_ON_IGNORED_PROPERTIES)
                .convertValue(parsedNodes, Response.class);
orly.sharon
  • 149
  • 1
  • 9
0

In my case it helped to create a constructor with a String argument and to annotate it with @JsonCreator:

    @JsonCreator
    public MyClass(String value) throws JsonProcessingException {
        MyClass root = objectMapper.readValue(value, MyClass.class);
        this.inst = root.getInst();
    }

siom
  • 1,610
  • 1
  • 14
  • 21
0

The leading cause behind the exception is trying to perform deserialization using double quotes ”…” instead of bracelets {…}.

Jackson interprets the double quotes as a string. So it expects the target object to have a string argument constructor or a factory method. Hence the error no String-argument constructor/factory method.

Azhwani
  • 79
  • 1
  • 3
  • 7
-3
mapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);

My code work well just as the answer above. The reason is that the json from jackson is different with the json sent from controller.

String test1= mapper.writeValueAsString(result1);

And the json is like(which can be deserialized normally):

{"code":200,"message":"god","data":[{"nics":null,"status":null,"desktopOperatorType":null,"marker":null,"user_name":null,"user_group":null,"user_email":null,"product_id":null,"image_id":null,"computer_name":"AAAA","desktop_id":null,"created":null,"ip_address":null,"security_groups":null,"root_volume":null,"data_volumes":null,"availability_zone":null,"ou_name":null,"login_status":null,"desktop_ip":null,"ad_id":null},{"nics":null,"status":null,"desktopOperatorType":null,"marker":null,"user_name":null,"user_group":null,"user_email":null,"product_id":null,"image_id":null,"computer_name":"BBBB","desktop_id":null,"created":null,"ip_address":null,"security_groups":null,"root_volume":null,"data_volumes":null,"availability_zone":null,"ou_name":null,"login_status":null,"desktop_ip":null,"ad_id":null}]}

but the json send from the another service just like:

{"code":200,"message":"查询桌面列表成功","data":[{"nics":"","status":"","metadata":"","desktopOperatorType":"","marker":"","user_name":"csrgzbsjy","user_group":"ADMINISTRATORS","user_email":"","product_id":"","image_id":"","computer_name":"B-jiegou-all-15","desktop_id":"6360ee29-eb82-416b-aab8-18ded887e8ff","created":"2018-11-12T07:45:15.000Z","ip_address":"192.168.2.215","security_groups":"","root_volume":"","data_volumes":"","availability_zone":"","ou_name":"","login_status":"","desktop_ip":"","ad_id":""},{"nics":"","status":"","metadata":"","desktopOperatorType":"","marker":"","user_name":"glory_2147","user_group":"ADMINISTRATORS","user_email":"","product_id":"","image_id":"","computer_name":"H-pkpm-all-357","desktop_id":"709164e4-d3e6-495d-9c1e-a7b82e30bc83","created":"2018-11-09T09:54:09.000Z","ip_address":"192.168.2.235","security_groups":"","root_volume":"","data_volumes":"","availability_zone":"","ou_name":"","login_status":"","desktop_ip":"","ad_id":""}]}

You can notice the difference when dealing with the param without initiation. Be careful

Herman XU
  • 7
  • 1
  • 1