141

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?

0

16 Answers 16

199

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

Sign up to request clarification or add additional context in comments.

2 Comments

@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
For anybody curious why this is the case, it's buried in the docs: fasterxml.github.io/jackson-databind/javadoc/2.8/com/fasterxml/… - "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."
67

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.

2 Comments

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 this exception won’t be thrown if the input doesn’t have any empty string.
26

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.

1 Comment

this is so precise reason for this exception! I straight away foound out the bug after reading this comment!! Ty!
8

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;
}

Comments

7

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

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

instead of

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

Comments

5

Use objectMapper.readTree(payload).asText()

None of the previous answers were addressing the issue in my case, Json object in string format

"{ \"fieldOne\": 1001, \"fieldTwo\": 1000, \"fieldThree\": \"true\" }"

The exception explicitly mentioning of the point there is no suitable constructor found capable for constructing from the Json String format.

One practice is to resolve this is to use objectMapper.readTree(payload).asText() which converts the payload to plain string format

"{     "fieldOne": 1001,     "fieldTwo": 1000,     "fieldThree": "true" }"

Finally constructing the desire object as

objectMapper.readValue(objectMapper.readTree(payload).asText() , type)

Comments

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);

Comments

2

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"
  }
}

1 Comment

Actually helped me figure out how to query an undocumented API, thanks
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);

Comments

1

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();
    }

Comments

1

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.

Comments

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);

Comments

0

Remove additional inverted commas and you object mapper is an escaped string so pls unescape it.

 LanguageCodeValues[] languageCodeList;
    ObjectMapper om = new ObjectMapper();
    try {
        languageCodeValues = modifyStringToParse(languageCodeValues);
        languageCodeList = om.readValue(languageCodeValues, LanguageCodeValues[].class);
        return languageCodeList;
    } catch (JsonProcessingException e) {
        log.error("CustomDeserializer :: getLanguageCodeValues() :: JSON PROCESSING EXCEPTION", e);
        throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, MESSAGE.INTERNAL_SERVER_ERROR);
    }



public static String modifyStringToParse(String jsonString){
    jsonString = StringEscapeUtils.unescapeJson(jsonString);
    jsonString = jsonString.substring(1, jsonString.length()-1);
    return jsonString;
}

Comments

0

For my case, I encountered the same issue and resolved it with the method below. The toClass method is generic and responsible for converting data of any type to the specific instance.

By adding the code snippets below, my string data was successfully parsed and deserialized

if (data instanceof String) {
        return objectMapper.readValue((String) data, clazz);  
    }

Full code snippet:


public static <T> T toClass(Object data, Class<T> clazz) throws JsonProcessingException {
    if (data == null) {
        return null;  
    }

    if (data instanceof String) {
        return objectMapper.readValue((String) data, clazz);  
    }

    return objectMapper.convertValue(data, clazz);
}

Comments

0

I experienced this error for yet another reason. I was building the xml mapper incorrectly resulting in my settings being ignored.

Here is what I was trying to do:

public static final XmlMapper XML_MAPPER = XmlMapper.xmlBuilder()
            .addModule(JACKSON_XML_MODULE)
            .nameForTextElement("_")
            .configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true)
            .configure(FromXmlParser.Feature.EMPTY_ELEMENT_AS_NULL, true)
            .build();

This does not work!!! This IGNORES the Jackson xml module.

Here is the corrected code:


JacksonXmlModule JACKSON_XML_MODULE = new JacksonXmlModule();
JACKSON_XML_MODULE.setDefaultUseWrapper(false);

XmlFactory xmlFactory = new XmlFactory();
xmlFactory.setXMLTextElementName("_");
XmlMapper xmlMapper = new XmlMapper(xmlFactory, JACKSON_XML_MODULE);
        xmlMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);     xmlMapper.configure(FromXmlParser.Feature.EMPTY_ELEMENT_AS_NULL, true);

This is now working fine.

XML_MAPPER.writeValueAsString(theObj);

Previous to the fix:

<PaginationDetail>
  <PaginationDetail/>    <!-- inner elements -->
  <PaginationDetail/>
</PaginationDetail>

Fixed:

  <PaginationDetail/>
  <PaginationDetail/>

Comments

-2
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

1 Comment

What does your answer add that mine doesn’t from 1.5 years ago?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.