99

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,651
  • 1
  • 14
  • 18

9 Answers9

121

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
  • 2,426
  • 3
  • 13
  • 33
  • 1
    @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
  • 3
    Had the same exception. This answer helped me. – ald.li Aug 12 '19 at 20:37
  • 3
    Had the same exception. This answer solved my problem. – arcy Oct 03 '19 at 01:05
  • 13
    It's one of those issues... Thank you! – Paweł Nadolski Oct 10 '19 at 10:38
  • 3
    This answer has helped me twice in the last week. (shame on me) – Ari Bustamante Apr 06 '20 at 16:37
  • 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
58

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,634
  • 4
  • 30
  • 43
Abhijit Sarkar
  • 19,114
  • 16
  • 94
  • 178
  • 6
    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
11

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
  • 111
  • 1
  • 3
3

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

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

instead of

{ "foo": { "value": 42 } }
Matthias
  • 11,682
  • 4
  • 46
  • 88
2

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
  • 28,850
  • 10
  • 94
  • 99
Abhilash Ranjan
  • 655
  • 7
  • 8
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
  • 129
  • 1
  • 9
0

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
  • 451
  • 4
  • 3
0

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
  • 444
  • 4
  • 11
-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
  • 9
  • 1
  • 1