3

Have an array, when the size is 1, the json data I received does NOT contains []; like

{"firstname":"tom"}

when the size is larger than 1, the data I received contains [], like

[{"firstname":"tom"},{"firstname":"robert"}]

Currently my class contains an array property

String[] firstname;
//getter setter omit here

Code to handle this likes

ObjectMapper mapper = new ObjectMapper();    
MyClass object = mapper.readValue(json, MyClass.class);

When the size is larger than 1, the deserialization works. However when size is 1, the deserialization failed.

I am currently using jackson, any solution for this problem?

I am wondering if jackson/gson or any other library can handle this?

5
  • Your question is confusing; please elaborate, with an example. Commented Mar 7, 2011 at 11:25
  • Where do you receive the data? How did you print those lines? What piece of code did you use? Anything at all? Commented Mar 7, 2011 at 11:31
  • add details. The data coming from a web service. Commented Mar 7, 2011 at 11:32
  • I don't think most libraries have default handling for this, since this is not a good way to model your data. If possible, I would fix the data to use arrays and not "optimize" for single element case. Commented Mar 7, 2011 at 20:30
  • 1
    This issue is happening for me with Jackson on the @ResponseBody where I have no control over it. Did you ever figure this out? Commented Apr 8, 2011 at 5:17

5 Answers 5

4

For Jackson specifically, your best bet would to first bind to a JsonNode or Object, like:

Object raw = objectMapper.readValue(json, Object.class); // becomes Map, List, String etc

and then check what you got, bind again:

MyClass[] result;
if (raw instanceof List<?>) { // array
  result = objectMapper.convertValue(raw, MyClass[].class);
} else { // single object
  result = objectMapper.convertValue(raw, MyClass.class);
}

But I think JSON you are getting is bad -- why would you return an object, or array, intead of just array of size 1? -- so if at all possible, I'd rather fix JSON first. But if that is not possible, this would work.

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

Comments

2

Here's how to do it with GSON. Let's assume this object structure:

public class Group{

    public Group(final List<Person> members){
        this.members = members;
    }

    private final List<Person> members;
}

public class Person{

    public Person(final String firstName, final String lastName){
        this.firstName = firstName;
        this.lastName = lastName;
    }

    private final String firstName;
    private final String lastName;
}

Here's a deserializer that understands single Person entries as well as arrays of them:

public class GroupDeserializer implements JsonDeserializer<Group>{

    @Override
    public Group deserialize(final JsonElement json,
        final Type typeOfT,
        final JsonDeserializationContext context) throws JsonParseException{
        List<Person> members;
        if(json.isJsonArray()){
            final JsonArray array = json.getAsJsonArray();
            members = new ArrayList<Person>(array.size());
            for(final JsonElement personElement : array){
                members.add(getSinglePerson(personElement, context));
            }
        } else{
            members =
                Collections.singletonList(getSinglePerson(json, context));
        }
        return new Group(members);
    }

    private Person getSinglePerson(final JsonElement element,
        final JsonDeserializationContext context){
        final JsonObject personObject = element.getAsJsonObject();
        final String firstName =
            personObject.getAsJsonPrimitive("firstname").getAsString();
        final String lastName =
            personObject.getAsJsonPrimitive("lastname").getAsString();
        return new Person(firstName, lastName);
    }

}

And here you can find the necessary Configuration to use this

4 Comments

Good example (and also of why Java makes me sad :-)
Keep in mind that this is just Gson approach, other packages have other solutions.
Note that with a solution like this, it's not necessary to also "manually" deserialize the Person objects, since there is nothing about Person that requires custom handling. Just use Person person = context.deserialize(json, Person.class); (Assuming a naming policy to match JSON element names to Java field names were also used.)
@pro b: Cool, nice to know. I'm rather new to Gson myself
1

edit: I guess you would then just extract a JsonElement and check it's isJsonArray() and/or isJsonObject(). Then, just call getAsJsonArray() or getAsJsonObject().

Old answer: Why not just try to extract the array and catch the JsonParseException if it fails. In the catch block, try to extract an object instead.

I know it's not pretty but it should work.

2 Comments

I really don't want to handle such exception one by one. I believe this is a common case that should have an elegant solution.
Fair enough. Though according to this, it's only one exception.
1

I have faced the same issue when I am trying to deserialize the JSON object which was constructed from XML. (XML-JSON). After quite a bit of research, found that we have a simple fix.

Just set the feature : ACCEPT_SINGLE_VALUE_AS_ARRAY.

ObjectMapper mapper = new ObjectMapper(); mapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);

For more info : http://fasterxml.github.com/jackson-databind/javadoc/2.0.0/com/fasterxml/jackson/databind/DeserializationFeature.html#ACCEPT_SINGLE_VALUE_AS_ARRAY

1 Comment

This worked for me in a spring environment. Seems the simplest resolution.
0

In the first instance it looks like an object, in the second instance it looks like an array of objects (which it sounds like you are expecting).

JSON encoding libraries typically have a "force array" option for cases like this. Failing that, on your client you could check the JSON response and if it's not an array, push the returned objected into an new array.

1 Comment

Can I "force array" from client side? I cannot control server response.

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.