As I indicated in the comment to the question here is the test that shows how to do this:
public class JSONTest {
public static class FullyTypedThing {
public int id;
public String name;
public List<Photos> photos = Lists.newArrayList();
public String username;
public static class Photos {
public String name;
public String url;
}
}
private static final String json = "[ { \"id\" : 33147, \"name\" : \"Refinancing\", \"photos\" : [ {"
+ "\"name\" : \"347.png\", \"url\" : \"/loans/568/photos/092\" } ],"
+ "\"username\" : \"zach1985\"} , { \"id\" : 7693, \"name\" : \"Stuff\","
+ "\"photos\" : [ { \"name\" : \"newpic1.png\", \"url\" : \"/loans/123446/photos/890\" } ],"
+ "\"username\" : \"sengaia\" } ]";
@Test
public void roundtrip() throws IOException {
ObjectMapper mapper = new ObjectMapper();
List<FullyTypedThing> res = Lists.newArrayList(
mapper.readValue(json, FullyTypedThing[].class));
assertEquals(2, res.size());
assertEquals(33147, res.get(0).id);
assertEquals("Refinancing", res.get(0).name);
assertEquals("347.png", res.get(0).photos.get(0).name);
assertEquals("/loans/568/photos/092", res.get(0).photos.get(0).url);
assertEquals(7693, res.get(1).id);
assertEquals("Stuff", res.get(1).name);
assertEquals("newpic1.png", res.get(1).photos.get(0).name);
assertEquals("/loans/123446/photos/890", res.get(1).photos.get(0).url);
assertEquals("[{\"id\":33147,\"name\":\"Refinancing\",\"photos\":"
+ "[{\"name\":\"347.png\",\"url\":\"/loans/568/photos/092\"}],"
+ "\"username\":\"zach1985\"},{\"id\":7693,\"name\":\"Stuff\","
+ "\"photos\":[{\"name\":\"newpic1.png\",\"url\":\"/loans/123446/photos/890\"}],"
+ "\"username\":\"sengaia\"}]", mapper.writeValueAsString(res));
}
}
Answer from Oleg Sklyar on Stack OverflowHelp deserializing a JSON array of arrays in DSL-JSON
How to Deserialize Array of JSON Objects in Java - Stack Overflow
java - How deserialize json object array as array of json strings? - Stack Overflow
Deserialize a JSON array into a Java object (Not a List nor a Collection) using Jackson - Stack Overflow
I want to deserialize this data:
[[ 1, 1, "Neil Peart", 67, "189 Carlton Street", "M5A 2K7", 21.12 ], [ 2, 2, "Geddy Lee", 67, "189 Carlton Street", "M5A 2K7", -21.12 ], [ 3, 3, "Alex Lifeson", 67, "189 Carlton Street", "M5A 2K7", 211.2 ]]
When I use this:
ObjectMapper mapper = new ObjectMapper(); byte[] input = jsonString.getBytes( StandardCharsets.US_ASCII ); com.google.gson.JsonObject dslObject1 = mapper.readValue( jsonString, com.google.gson.JsonObject.class );
It throws a JsonProcessingException with the message:
Cannot deserialize value of type `com.google.gson.JsonObject` from Array value (token `JsonToken.START_ARRAY`)
at [Source: (String)"[[1,1,"Neil Peart",67,"189 Carlton Street","M5A 2K7",21.12]...
When I use this:
byte[] input = jsonString.getBytes( StandardCharsets.US_ASCII ); com.dslplatform.json.JsonObject dslObject2 = dslJson.deserialize( com.dslplatform.json.JsonObject.class, input, input.length );`
it throws an IOException with this message:
Unable to find reader for provided type: interface com.dslplatform.json.JsonObject and fallback serialization is not registered.
Any suggestions?
This may help you:
import java.io.IOException;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.databind.ObjectMapper;
public class Test {
public static void main(String[] args) throws IOException {
ObjectMapper mapper = new ObjectMapper();
String json = "[\r\n" + " {\r\n" + " \"example\": \"test\",\r\n" + " \"is_valid\": true\r\n"
+ " },\r\n" + " {\r\n" + " \"example\": \"test2\",\r\n" + " \"is_valid\": true\r\n"
+ " }\r\n" + "]";
Example[] ex = mapper.readValue(json, Example[].class);
}
}
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({ "example", "is_valid" })
class Example {
@JsonProperty("example")
private String example;
@JsonProperty("is_valid")
private Boolean isValid;
public String getExample() {
return example;
}
@JsonProperty("example")
public void setExample(String example) {
this.example = example;
}
@JsonProperty("is_valid")
public Boolean getIsValid() {
return isValid;
}
@JsonProperty("is_valid")
public void setIsValid(Boolean isValid) {
this.isValid = isValid;
}
}
When response is a JSON Object you can use default bean deserialiser. In case it is a JSON Array you can read it as array and create response object manually. Below you can find example custom deserialiser and BeanDeserializerModifier which is used to register custom deserialiser. BeanDeserializerModifier allows to use default deserialiser when JSON payload fits POJO model:
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.DeserializationConfig;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.BeanDeserializer;
import com.fasterxml.jackson.databind.deser.BeanDeserializerBase;
import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier;
import com.fasterxml.jackson.databind.exc.MismatchedInputException;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.type.CollectionType;
import java.io.File;
import java.io.IOException;
import java.util.List;
public class JsonApp {
public static void main(String[] args) throws Exception {
File jsonFile = new File("./resource/test.json").getAbsoluteFile();
SimpleModule module = new SimpleModule();
module.setDeserializerModifier(new BeanDeserializerModifier() {
@Override
public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
if (beanDesc.getBeanClass() == Response.class) {
return new ResponseJsonDeserializer((BeanDeserializerBase) deserializer);
}
return super.modifyDeserializer(config, beanDesc, deserializer);
}
});
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(module);
System.out.println(mapper.readValue(jsonFile, Response.class));
}
}
class ResponseJsonDeserializer extends BeanDeserializer {
private final BeanDeserializerBase baseDeserializer;
protected ResponseJsonDeserializer(BeanDeserializerBase src) {
super(src);
this.baseDeserializer = src;
}
@Override
public Response deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
if (p.currentToken() == JsonToken.START_OBJECT) {
return (Response) baseDeserializer.deserialize(p, ctxt);
}
if (p.currentToken() == JsonToken.START_ARRAY) {
CollectionType collectionType = ctxt.getTypeFactory().constructCollectionType(List.class, Item.class);
JsonDeserializer<Object> deserializer = ctxt.findRootValueDeserializer(collectionType);
List<Item> results = (List<Item>) deserializer.deserialize(p, ctxt);
Response response = new Response();
response.setCount(results.size());
response.setResults(results);
return response;
}
throw MismatchedInputException.from(p, Response.class, "Only object or array!");
}
}
class Response {
private int count;
private List<Item> results;
// getters, setters, toString
}
class Item {
private String example;
@JsonProperty("is_valid")
private boolean isValid;
// getters, setters, toString
}
Above code for JSON Object payload prints:
Response{count=5, results=[Item{example='test', isValid=true}, Item{example='test2', isValid=true}]}
And for JSON Array payload prints:
Response{count=2, results=[Item{example='test', isValid=true}, Item{example='test2', isValid=true}]}
I am getting a null pointer exception when trying to run the program.
Cannot invoke "java.util.Collection.add(Object)" because "this.contacts" is null java.lang.NullPointerException: Cannot invoke "java.util.Collection.add(Object)" because "this.contacts" is null
I have a json file that is structured like this:
{
"id": 1,
"name": {
"first": "Yong",
"last": "Mook Kim"
},
"contact": [
{
"type": "phone/home",
"ref": "111-111-1234"
},
{
"type": "phone/work",
"ref": "222-222-2222"
}
]
}ContactsCustomDeserializer.java
package jacksontreedeserializer;
import com.fasterxml.jackson.core.JacksonException;
import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import jacksontree.Contact;
import jacksontree.Contacts;
import java.io.IOException;
public class ContactsCustomDeserializer extends StdDeserializer<Contacts> {
public ContactsCustomDeserializer(){
this(null);
}
public ContactsCustomDeserializer(Class clz){
super(clz);
}
@Override
public Contacts deserialize(JsonParser parser, DeserializationContext ctxt) throws IOException, JacksonException {
Contacts contacts = new Contacts();
try {
JsonNode root = parser.getCodec().readTree(parser);
JsonNode contactRoot = root.path("contact");
if(contactRoot.isArray()){
for(JsonNode node: contactRoot){
String type = node.path("type").asText();
String ref = node.path("red").asText();
Contact contact = new Contact(type,ref);
contacts.addContact(contact);
}
}
}
catch(JsonGenerationException e){
e.printStackTrace();
}
return contacts;
}
}Contact.java
package jacksontree;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import jacksontreedeserializer.ContactsCustomDeserializer;
public class Contact {
@JsonProperty("type")
private String type;
@JsonProperty("ref")
private String ref;
public Contact() {}
public Contact(String type, String ref) {
this.type = type;
this.ref = ref;
}
@Override
public String toString() {
return "Contact{" +
"type='" + type + '\'' +
", ref='" + ref + '\'' +
'}';
}
}Contacts.java
package jacksontree;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.Collection;
import java.util.List;
public class Contacts {
@JsonProperty("contacts")
private Collection<Contact> contacts;
public Contacts(Collection<Contact> contacts){
this.contacts = contacts;
}
public Contacts(){}
public void addContact(Contact contact){
contacts.add(contact);
}
@Override
public String toString() {
return "Contacts{" +
"contacts=" + contacts +
'}';
}
}Main.java
@Test
void deserialize() throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
SimpleModule simpleModule = new SimpleModule();
simpleModule.addDeserializer(Contacts.class, new ContactsCustomDeserializer());
objectMapper.registerModule(simpleModule);
Contacts contacts = objectMapper.readValue(new File("/Users/foo/Downloads/LearningJackson/src/main/resources/data/user.json"), Contacts.class);
System.out.println(contacts);
}If json data is of the form:
[ {...}, {...}, {...} ]
You got to use add another class say 'wrapper':
@JsonIgnoreProperties(ignoreUnknown = true)
public class ListDto extends ArrayList<GroupDto> {
public ListDto() {
}
}
And use this class while deserailizing. This approach worked for me.
You just need to specify the @JsonDeserialize(contentAs=GroupDto.class) in your setter. Serialization is always on get desserialization is always on set, or if you prefer you can specify both on the field.
Documentation for Serialize and Deserialize
Code sample:
import java.io.IOException;
import java.util.List;
import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.annotate.JsonDeserialize;
public class JacksonDeserialize {
public static class ModelClass {
private String name;
public ModelClass() {
}
public String getName() {
return name;
}
public void setName(final String name) {
this.name = name;
}
public ModelClass(final String name) {
super();
this.name = name;
}
@Override
public String toString() {
return "ModelClass [name=" + name + "]";
}
}
public static class ListModelClass {
private List<ModelClass> list;
@JsonDeserialize(contentAs = ModelClass.class)
public void setList(final List<ModelClass> list) {
this.list = list;
}
@Override
public String toString() {
return "ListModelClass [list=" + list + "]";
}
}
public static void main(final String[] args) throws JsonGenerationException, JsonMappingException, IOException {
ObjectMapper objectMapper = new ObjectMapper();
System.out.println(objectMapper.readValue("{\"list\":[{\"name\":\"name1\"},{\"name\":\"name2\"}]}",
ListModelClass.class));
}
}
First create a mapper :
import com.fasterxml.jackson.databind.ObjectMapper;// in play 2.3
ObjectMapper mapper = new ObjectMapper();
As Array:
MyClass[] myObjects = mapper.readValue(json, MyClass[].class);
As List:
List<MyClass> myObjects = mapper.readValue(jsonInput, new TypeReference<List<MyClass>>(){});
Another way to specify the List type:
List<MyClass> myObjects = mapper.readValue(jsonInput, mapper.getTypeFactory().constructCollectionType(List.class, MyClass.class));
From Eugene Tskhovrebov
List<MyClass> myObjects = Arrays.asList(mapper.readValue(json, MyClass[].class))
This solution seems to be the best for me.
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));
It is easy to serialize, but not so easy to deserialize in such manner;
The following class can be serialized into an array of strings as in your question with @JsonValue;
public class Person {
private String firstName;
private String lastName;
//getter,setter,constructors
@JsonValue
public List<String> craeteArr() {
return Arrays.asList(this.firstName, this.lastName);
}
}
But to deserialize, I had to create a wrapper class, and use custom deserialization with @JsonDeserialize;
public class PersonWrapper {
@JsonDeserialize(using = CustomDeserializer.class)
private List<Person> persons;
//getter,setter,constructors
}
and the custom deserializer itself;
public class CustomDeserializer extends JsonDeserializer<List<Person>> {
@Override
public List<Person> deserialize(JsonParser jsonParser, DeserializationContext context) throws IOException {
JsonNode node = jsonParser.readValueAsTree();
ObjectMapper mapper = new ObjectMapper();
return IntStream.range(0, node.size()).boxed()
.map(i -> {
try {
List<String> values = mapper.readValue(node.get(i).toString(), List.class);
return new Person().setFirstName(values.get(0)).setLastName(values.get(1));
} catch (IOException e) {
throw new RuntimeException();
}
}).collect(Collectors.toList());
}
}
You need to put proper validation in deserializer logic to check that each mini-array contains exactly two values, but this works well.
I'd rather use these steps, and maybe to hide @JsonDeserialize, I'd do the following;
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonDeserialize(using = CustomDeserializer.class)
public @interface AcceptPersonAsArray {}
So you can use some custom annotation in PersonWrapper
public class PersonWrapper {
@AcceptPersonAsArray
private List<Person> persons;
//getter,setter,constructors
}