MeanwhileInHell
MeanwhileInHell

Reputation: 7053

Custom Jackson Deserializer to omit mapping of certain objects

I have a large JSON file that is made up of structures that I am mapping into POJOs, and then storing in a Collection. The structure is similar to this:

[
{
    "id": 1234,
    "file": "C:\\Programs\\Program1.exe",
    "exists": true
}
{
    "id": 5678,
    "file": "C:\\Programs\\Program2.exe",
    "exists": false
}
...
]

Using the Jackson streaming API I have got all these structures read, and the POJOs stored in a Collection successfully. My POJO class looks like this:

public class Programs
{
    @JsonProperty("id")
    private Integer id;

    @JsonProperty("file")
    private String file;

    @JsonProperty("exists")
    private Boolean exists;

    @JsonGetter("id")
    public Integer getId()
    {
      return id;
    }

    @JsonGetter("file")
    public String getFile()
    {
      return file;
    }

    @JsonGetter("exists")
    public Boolean getExists()
    {
      return exists;
    }
}

However, I want to omit any structures that have "exists" set to false during the deserialization process so that no POJO is ever created for them. So I wrote a custom deserializer with the help of this SO question [ How do I call the default deserializer from a custom deserializer in Jackson ], with my overridden deserialize looking like:

@Override
public Programs deserialize(JsonParser parser, DeserializationContext context)
   throws IOException
{
    Programs programs = (Programs)defaultDeserializer.deserialize(parser, context);

    if (!programs.getExists())
    {
        throw context.mappingException("[exists] value is false.");
    }
    return programs;
}

However, when I run some unit tests, I get the following error:

"Can not deserialize instance of java.util.ArrayList out of START_OBJECT token"
message was "Class com.myprogram.serializer.ProgramsJsonDeserializer
has no default (no arg) constructor"

(Adding a no arg constructor gives the error that StdDeserializer does not have a default constructor.)

Is this the correct approach to achieving what I am trying to do? And does anyone know why I get this error message?

Upvotes: 1

Views: 954

Answers (1)

Wilson
Wilson

Reputation: 11637

I want to omit any structures that have "exists" set to false during the deserialization process so that no POJO is ever created for them.

I think your objective is to retrieve a list of Programs instance that only have exists set to true after derserialization. A customized CollectionDeserializer to filter those unwanted instance may help:

public class ProgramsCollectionHandler extends SimpleModule {
    private static class ProgramsCollectionDeserializer extends CollectionDeserializer {

        public ProgramsCollectionDeserializer(CollectionDeserializer deserializer) {
            super(deserializer);
        }

        private static final long serialVersionUID = 1L;

        @Override
        public Collection<Object> deserialize(JsonParser parser, DeserializationContext context)
                throws IOException, JsonProcessingException {
            Collection<Object> result = super.deserialize(parser, context);
            Collection<Object> filteredResult = new ArrayList<Object>();
            for (Object o : result) {
                if (o instanceof Programs) {
                    final Programs programs = (Programs) o;
                    if (programs.exists) {
                        filteredResult.add(programs);
                    }
                }
            }
            return filteredResult;
        }

        @Override
        public CollectionDeserializer createContextual(
                DeserializationContext context,
                BeanProperty property) throws JsonMappingException {
            return new ProgramsCollectionDeserializer(super.createContextual(context, property));
        }
    }

    private static final long serialVersionUID = 1L;

    @Override
    public void setupModule(Module.SetupContext context) {
        super.setupModule(context);
        context.addBeanDeserializerModifier(new BeanDeserializerModifier() {
            @Override
            public JsonDeserializer<?> modifyCollectionDeserializer(
                    DeserializationConfig config, CollectionType type,
                    BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
                if (deserializer instanceof CollectionDeserializer) {
                    return new ProgramsCollectionDeserializer(
                            (CollectionDeserializer) deserializer);
                }
                return super.modifyCollectionDeserializer(config, type, 
                        beanDesc, deserializer);
            }
        });
    }
}

After that, your can register it into your object mapper:

ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new ProgramsCollectionHandler());

"Can not deserialize instance of java.util.ArrayList out of START_OBJECT token" message was "Class com.myprogram.serializer.ProgramsJsonDeserializer has no default (no arg) constructor" (Adding a no arg constructor gives the error that StdDeserializer does not have a default constructor.)

This may be because your constructor cannot be accessed. For example, your deserializer is implemented as a non-static inner class.

Upvotes: 2

Related Questions