Amit
Amit

Reputation: 455

Can anyone suggest some best practices for supporting partial representations in restful service

I am implementing restful API where i would like the caller to specify the specific properties that one likes to be serialized something like /dogs/1?fields=name,breed. So if the dog has many more properties other than name and breed and I am using a standard serialization library like jackson how can i tell jackson to ignore the rest of the properties while serializing in runtime?

In other words what is the way to tell jackson to dynamically ignore some properties at runtime?

Upvotes: 0

Views: 306

Answers (2)

romanvintonyak
romanvintonyak

Reputation: 351

I'd recommend to take a look at http://flexjson.sourceforge.net/ (in particular section Working With Includes/Excludes)

Example:

    public String toJsonWithIncludes(Object obj, String...includes) {
            return new JSONSerializer()
                    .include(includes)
                    .exclude("data.*","*.class")
                    .serialize(obj);
    }

...
toJsonWithIncludes(obj, "name","breed");

Upvotes: 0

Michał Ziober
Michał Ziober

Reputation: 38655

You did not specify version of Jackson library, so I assume that you are using 2.2.0 or newest. In Jackson this is not easy task to do but we can do it, for example, with BasicClassIntrospector. Below source code shows how it should look like. For example we have one POJO class:

class User {

    private String name;
    private String surname;

    // getters/setters
}

We have to define new BasicClassIntrospector which returns bean definition object only with this fields which we need:

class ExclusionAnnotationIntrospector extends BasicClassIntrospector {

    private Collection<String> properties;

    public ExclusionAnnotationIntrospector(Collection<String> properties) {
        this.properties = properties;
    }

    @Override
    public BasicBeanDescription forSerialization(SerializationConfig serializationConfig,
            JavaType javaType, MixInResolver mixInResolver) {

        BasicBeanDescription beanDesc = super.forSerialization(serializationConfig, javaType,
                mixInResolver);
        deleteUnwantedProperties(beanDesc);

        return beanDesc;
    }

    private void deleteUnwantedProperties(BasicBeanDescription beanDesc) {
        List<BeanPropertyDefinition> beanProperties = new ArrayList<BeanPropertyDefinition>(
                beanDesc.findProperties());
        for (BeanPropertyDefinition bpd : beanProperties) {
            String name = bpd.getName();
            if (!properties.contains(name)) {
                beanDesc.removeProperty(name);
            }
        }
    }
}

Now, we have to create new ObjectMapper class with above introspector. We have to define properties we need in constructor:

class CustomObjectMapper extends ObjectMapper {

    public CustomObjectMapper(Collection<String> properties) {
        this._serializationConfig = getSerializationConfig().with(
                new ExclusionAnnotationIntrospector(properties));
    }
}

And, finally, example usage:

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.introspect.BasicBeanDescription;
import com.fasterxml.jackson.databind.introspect.BasicClassIntrospector;
import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;

public class JacksonProgram {

    public static void main(String[] args) throws Exception {
        User user = new User();
        user.setName("Tom");
        user.setSurname("Garnier");

        CustomObjectMapper onlyNameSerializer = new CustomObjectMapper(toSet("name"));
        CustomObjectMapper onlySurnameSerializer = new CustomObjectMapper(toSet("surname"));
        CustomObjectMapper fullUserSerializer = new CustomObjectMapper(toSet("name", "surname"));

        System.out.println(onlyNameSerializer.writeValueAsString(user));
        System.out.println(onlySurnameSerializer.writeValueAsString(user));
        System.out.println(fullUserSerializer.writeValueAsString(user));
    }

    private static Set<String> toSet(String... params) {
        HashSet<String> set = new HashSet<String>();
        for (String item : params) {
            set.add(item);
        }

        return set;
    }
}

Above program prints:

{"name":"Tom"}
{"surname":"Garnier"}
{"name":"Tom","surname":"Garnier"}

Upvotes: 3

Related Questions