First of all, If you registered your custom deserializer using annotation on the bean CustomClass then the deserializer should handle one instance of CustomClass and not a collection and thus should be defined:
public class MyCustomDeserializer extends JsonDeserializer<CustomClass> {
@Override
public CustomClass deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException
{
...
}
}
and now you can use Jackson's type factory to pass the mapper the required type information
JavaType customClassCollection = objectMapper.getTypeFactory().constructCollectionType(List.class, CustomClass.class);
List<CustomClass> beanList = (List<CustomClass>)objectMapper.readValue(stringBean, customClassCollection);
Answer from Sharon Ben Asher on Stack OverflowFirst of all, If you registered your custom deserializer using annotation on the bean CustomClass then the deserializer should handle one instance of CustomClass and not a collection and thus should be defined:
public class MyCustomDeserializer extends JsonDeserializer<CustomClass> {
@Override
public CustomClass deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException
{
...
}
}
and now you can use Jackson's type factory to pass the mapper the required type information
JavaType customClassCollection = objectMapper.getTypeFactory().constructCollectionType(List.class, CustomClass.class);
List<CustomClass> beanList = (List<CustomClass>)objectMapper.readValue(stringBean, customClassCollection);
I worked it out by adding a custom deserializer to an attribute in my model class and using JsonDeserialize annotation's contentUsing() method, like so:
@JsonDeserialize(contentUsing = MyCustomDeserializer.class)
private List<CustomClass> customClassObjList;
where MyCustomDeserializer class is a custom Jackson JSON deserializer defined as:
public class MyCustomDeserializer extends JsonDeserializer<CustomClass> {
@Override
public CustomClass deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
...
}
}
java - Custom deserializer for any list in Jackson - Stack Overflow
Trying to deserialize a json array using a Jackson custom deserializer
java - How to create a custom deserializer in Jackson for a List containing generic type? - Stack Overflow
java - How to define a generic list deserializer through annotations with Jackson? - Stack Overflow
You can write a custom deserializer and implement deserialization logic in it, e.g.:
class ItemIdDeserialiser extends JsonDeserializer<Integer> {
@Override
public Integer deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
Object value = p.getCurrentValue();
//Check if it's Integer
if(value instanceof Integer){
return (Integer) value;
}
return null; //Or return first element if it's a non empty list
}
}
Once this is done, you can annotate the field with @JsonDeserialise to instruct jackson to use your class, e.g.:
class Item {
@JsonDeserialize(using = ItemIdDeserialiser.class)
private Integer id;
}
Update
If you just want to ignore the field in serialization/deserialization then you can annotate it with @JsonIgnore, e.g.
class Item {
@JsonIgnore
private Integer id;
}
Or even better, remove id from pojo and add @JsonIgnoreProperties on the class, e.g.:
@JsonIgnoreProperties(ignoreUnknown = true)
class Item {
}
It will automatically ignore the properties which are present in json but not found in class.
You can use a "HidableSerializer" for this and check the data during serialization
1. Create a IHidable interface
The interface has a isHidden method which is called during serialization
package ch.hasselba.jackson.test;
public interface IHidable {
public boolean isHidden();
}
2. Change your BadItem class
Add the interface and change the setter of id. When property id is deserialized, it is tested if it is an Integer. If not, the item is marked as bad.
package ch.hasselba.jackson.test;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
@JsonIgnoreProperties( {"hidden"} )
public class BadItem implements IHidable{
private Integer id;
public String name;
private boolean isBadItem;
public Integer getId(){
return id;
}
public void setId(Object value){
if( value instanceof Integer ){
this.id = (Integer) value;
}else{
this.isBadItem = true;
}
}
public boolean isHidden() {
return isBadItem;
}
}
3. Create a HidableSerializer
package ch.hasselba.jackson.test;
import java.io.IOException;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
@SuppressWarnings("serial")
public class HidableSerializer<T> extends StdSerializer<T> {
private JsonSerializer<T> defaultSerializer;
protected HidableSerializer(Class<T> t) {
super(t);
}
public JsonSerializer<T> getDefaultSerializer() {
return defaultSerializer;
}
public void setDefaultSerializer(JsonSerializer<T> defaultSerializer) {
this.defaultSerializer = defaultSerializer;
}
@Override
public void serialize(T value, JsonGenerator jgen, SerializerProvider provider)
throws IOException, JsonProcessingException {
if( value instanceof IHidable ){
IHidable hidableValue = (IHidable) value;
if( hidableValue.isHidden() )
return;
}
defaultSerializer.serialize(value, jgen, provider);
}
}
4. Register the HidableSerializer and that's it
package ch.hasselba.jackson.test;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
public class Demo {
@SuppressWarnings("serial")
public static void main(String[] args) {
// register the HidableSerializer
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(Include.NON_EMPTY);
mapper.registerModule(new SimpleModule() {
@Override
public void setupModule(SetupContext context) {
super.setupModule(context);
context.addBeanSerializerModifier(new BeanSerializerModifier() {
@Override
public JsonSerializer<?> modifySerializer(
SerializationConfig config, BeanDescription desc, JsonSerializer<?> serializer) {
if (BadItem.class.isAssignableFrom(desc.getBeanClass())) {
HidableSerializer ser = new HidableSerializer(BadItem.class);
ser.setDefaultSerializer(serializer);
return ser;
}
return serializer;
}
});
}
});
String content = "{ \"items\": [ { \"id\": 1, \"name\": \"Item1\" }, { \"id\": 2, \"name\": \"Item2\" }, { \"id\":[], \"name\": \"Item3\" } ]}";
// build the Object
BadList test = null;
try {
test = mapper.readValue(content, BadList.class);
} catch (Exception e) {
e.printStackTrace();
}
// and now convert it back to a String
String data = null;
try {
data = mapper.writeValueAsString(test);
} catch (Exception e) {
e.printStackTrace();
}
// print the result
System.out.println( data );
}
}
When changing the id "[]" to an Integer value, the Item is displayed, otherwise it is empty.
The result:
{"items":[{"id":1,"name":"Item1"},{"id":2,"name":"Item2"}]}
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);
}Besides JSON, Jackson also de/serializes from/to XML.
You'll need the following dependency:
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.6.3</version>
</dependency>
In addition to standard Jackson annotations and optional JAXB annotations (javax.xml.bind.annotation), Jackson provides some XML-specific annotations.
Consider the following POJO as example:
public class Simple {
public int x = 1;
public int y = 2;
}
You can serialize it to XML using:
ObjectMapper xmlMapper = new XmlMapper();
String xml = xmlMapper.writeValueAsString(new Simple());
The result is:
<Simple>
<x>1</x>
<y>2</y>
</Simple>
To deserialize:
ObjectMapper xmlMapper = new XmlMapper();
Simple value = xmlMapper.readValue("<Simple><x>1</x><y>2</y></Simple>", Simple.class);
Yes, there is JAXB.
Intro to JAXB
The reference implementation is called Project Kenai. It's part of Metro (JAX-WS reference implementation), but it can be used standalone.