Make Jackson Interpret Single JSON Object as Array with One Element

Make Jackson interpret single JSON object as array with one element

Try with DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY - it should work for you.

Example:

final String json = "{\"date\" : \"2013-05-11\",\"value\" : 123}";

final ObjectMapper mapper = new ObjectMapper()
.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
final List<Point> points = mapper.readValue(json,
new TypeReference<List<Point>>() {});

Jackson deserialize single item into list

You are not the first to ask for this problem. It seems pretty old.

After looking at this problem, you can use the DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY :

Look at the documentation :
http://fasterxml.github.io/jackson-databind/javadoc/2.5/com/fasterxml/jackson/databind/DeserializationFeature.html#ACCEPT_SINGLE_VALUE_AS_ARRAY

You need to add this jackson feature to your object mapper.

I hope it will help you.

Jackson library 1.x array if one element sometimes just a string

There's a specific config option even in ancient Jackson 1.8.2 that accomplishes exactly what you need.

You should configure your ObjectMapper instance to always deserialize JSON values as a List, no matter whether values come as an array or as a single element. Please see javadocs here for the deserialization feature you need to enable, and these other javadocs to see how to actually activate/deactivate a feature on an ObjectMapper instance.

ObjectMapper mapper = ...;
mapper = mapper.configure(DeserializationConfig.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);

Bear in mind that configure() method returns another instance of ObjectMapper.

Deserialize JSON array to a single Java object with Jackson

Thanks to bureaquete for suggestion to use custom Deserializer. But it was more suitable for me to register it with SimpleModule instead of @JsonDeserialize annotation. Below is complete JUnit test example:

@RunWith(JUnit4.class)
public class MapArrayToObjectTest {
private static ObjectMapper mapper;

@BeforeClass
public static void setUp() {
mapper = new ObjectMapper();
SimpleModule customModule = new SimpleModule("ExampleModule", new Version(0, 1, 0, null));
customModule.addDeserializer(Person.class, new PersonDeserializer());
mapper.registerModule(customModule);
}

@Test
public void wrapperDeserializationTest() throws IOException {
//language=JSON
final String inputJson = "{\"persons\": [[\"John\", \"Doe\"], [\"Jane\", \"Doe\"]]}";
PersonsListWrapper deserializedList = mapper.readValue(inputJson, PersonsListWrapper.class);
assertThat(deserializedList.persons.get(0).lastName, is(equalTo("Doe")));
assertThat(deserializedList.persons.get(1).firstName, is(equalTo("Jane")));
}

@Test
public void listDeserializationTest() throws IOException {
//language=JSON
final String inputJson = "[[\"John\", \"Doe\"], [\"Jane\", \"Doe\"]]";
List<Person> deserializedList = mapper.readValue(inputJson, mapper.getTypeFactory().constructCollectionType(List.class, Person.class));
assertThat(deserializedList.get(0).lastName, is(equalTo("Doe")));
assertThat(deserializedList.get(1).firstName, is(equalTo("Jane")));
}
}

class PersonsListWrapper {
public List<Person> persons;
}

class Person {
final String firstName;
final String lastName;

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

class PersonDeserializer extends JsonDeserializer<Person> {
@Override
public Person deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
JsonNode node = jp.readValueAsTree();
return new Person(node.get(0).getTextValue(), node.get(1).getTextValue());
}
}

Note that if you do not need wrapper object, you can deserialize JSON array
[["John", "Doe"], ["Jane", "Doe"]] directly to List<Person> using mapper as follows:

List<Person> deserializedList = mapper.readValue(inputJson, mapper.getTypeFactory().constructCollectionType(List.class, Person.class));

How to parse a JSON array of objects using Jackson?

The Employee POJO should look like this:

@JsonProperty("id")
private final List<String> ids;
@JsonProperty("firstname")
private final List<String> firstNames;
@JsonProperty("lastname")
private final List<String> lastNames;

@JsonCreator
public Employee(@JsonProperty(value = "id") List<String> ids, @JsonProperty(value = "firstname") List<String> firstNames, @JsonProperty(value = "lastname") List<String> lastNames) {
this.ids = ids;
this.firstNames = firstNames;
this.lastNames = lastNames;
}

//getters code

Then, you have an object Data:

@JsonProperty("date")
private final String date;
@JsonProperty("employee")
private final List<Employee> employees;

@JsonCreator
public Data(@JsonProperty(value = "date") String date, @JsonProperty(value = "employee") List<Employee> employees) {
this.date = date;
this.employees = employees;
}

//getters code

Finally, the whole Answer that you want to parse has this shape:

@JsonProperty("data")
private final Data data;

@JsonCreator
public Answer(@JsonProperty(value = "data") Data data) {
this.data = data;
}

//getter code

Once you have defined these 3 classes, then you will be able to do:

ObjectMapper objectMapper = new ObjectMapper();
Answer answer = objectMapper.readValue(yourStringAnswer, Answer.class);

Note: in your question, you are trying to parse an URL to an ObjectNode. I hardly doubt you would be able to do that.
I guess you want to perform the HTTP request to the URL, then getting the response stream and that's what you want to parse into Answer (not the URL itself).

Also, a few notes on the API response (in case you own it and so you can act on it):

  • All the lists would be more naturally declared with a plural name (e.g. employee should be employees)
  • The list of ids are numeric but are returned as strings. Also, why an employee would have a list of ids, and not a single id?
  • Why would an employee have a list of first names and last names? Shouldn't this be a simple string each (even if composed by more than one name)?
  • Use camel case (firstName, not firstname)
  • I don't see the point of putting everything into data, it may simply be the response containing date and employees

Jackson: Parsing an object with an array of objects

The easiest thing I can think to do is to make your java objects look like your Json. Because this Json has a wrapped inner object but not the outer object you would have to have a similar wrapping in java. It isn't elegant but works.

public class RecurringDetailsResult implements java.io.Serializable {

private Date creationDate;
private String shopperReference;
private List<RecurringDetailWrapper> details;
private String lastKnownShopperEmail;

// getters and setters here. No need for any @JsonGetter or @JsonSetter annotations
}

@JsonRootName("RecurringDetail")
public class RecurringDetailWrapper {

@JsonProperty("RecurringDetail")
RecurringDetail recurringDetail;

public RecurringDetail getRecurringDetail() {
return recurringDetail;
}

public void setRecurringDetail(RecurringDetail recurringDetail) {
this.recurringDetail = recurringDetail;
}
}

public class RecurringDetail implements java.io.Serializable {
private static final long serialVersionUID = 5302883242997268343L;

private String name;
private Date creationDate;
private Card card;
private AdditionalData additionalData;
private String socialSecurityNumber;
private String recurringDetailReference;
private String alias;
private String aliasType;
private String variant;
private String paymentMethodVariant;
private String firstPspReference;
private List<String> contractTypes;
private String acquirer;
private String acquirerAccount;

public class AdditionalData {
String cardBin;

public String getCardBin() {
return cardBin;
}

public void setCardBin(String cardBin) {
this.cardBin = cardBin;
}
}

// getters and setters here. No need for any @JsonGetter or @JsonSetter annotations

}

Then in your unit test:

@Test
public void testParseRecurringDetailResulte() throws IOException {

InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("ID41901838.json");

ObjectReader objectReader = mapper.readerFor(RecurringDetailsResult.class);
RecurringDetailsResult result = objectReader.readValue(inputStream);

if (result.getDetails() != null && !result.getDetails().isEmpty()) {
RecurringDetailWrapper detail = result.getDetails().get(0);
if (StringUtils.isEmpty(detail.getRecurringDetail().getRecurringDetailReference())) {
fail("Recurring detail does not contain any information.");
}
} else {
fail("No result details returned.");
}
}

I posted the full working code here:
https://github.com/teacurran/java-experiments/tree/master/stackoverflow-sandbox/src/main/java/com/wirelust/stackoverflowsandbox/ID41901838

Jackson desrialize when JsonProperty is sometimes array and sometimes a single Object

If you enable DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY both

{"result": []} and

{"result": {}} can be parsed as

class Result{
List result;
}

Documentation link

Feature that determines whether it is acceptable to coerce non-array
(in JSON) values to work with Java collection (arrays,
java.util.Collection) types. If enabled, collection deserializers will
try to handle non-array values as if they had "implicit" surrounding
JSON array. This feature is meant to be used for
compatibility/interoperability reasons, to work with packages (such as
XML-to-JSON converters) that leave out JSON array in cases where there
is just a single element in array. Feature is disabled by default.

Demo how to use it for OP input jsons and POJOs:

ObjectMapper mapper = new ObjectMapper()
mapper.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
Result result = mapper.readValue(Result.class;

Can be used with @JsonFormat above class or field if you don't like mapper version for some reason

@JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)

PS. Other SO questions about this problem: LINK

Jackson Don't parse the whole item if certain value is present in JSON Array

You can use Jackson to read a stream of JSON data. See this tutorial for details. This will allow you to decide on the fly whether or not to create any object being parsed.

Alternatively, check out javax.json.stream. This library allows pull parsing which gives you the control you want over parsing your JSON.

In general, the terms you are looking for are "push parsing" and "pull parsing". Use those to find other alternatives to accomplish what you want.



Related Topics



Leave a reply



Submit