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 beemployees
) - 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
, notfirstname
) - I don't see the point of putting everything into
data
, it may simply be the response containingdate
andemployees
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
Quickest Way to Find Missing Number in an Array of Numbers
Io Error: the Network Adapter Could Not Establish the Connection
Static Nested Class in Java, Why
Using Selenium Webdriver to Retrieve the Value of an HTML Input
Java Gui Frameworks. What to Choose? Swing, Swt, Awt, Swingx, Jgoodies, Javafx, Apache Pivot
When Overriding a Method, Why How to Increase Access But Not Decrease It
Java.Awt.Eventqueue.Invokelater Explained
Why Does the Ternary Operator Unexpectedly Cast Integers
Print an Integer in Binary Format in Java
Given Final Block Not Properly Padded
Alert Handling in Selenium Webdriver (Selenium 2) with Java
Reversing a Linked List in Java, Recursively
How to Inject Authenticationmanager Using Java Configuration in a Custom Filter