Ingoring certain fields at runtime

105 views
Skip to first unread message

mdd...@gmail.com

unread,
Mar 8, 2014, 4:06:32 AM3/8/14
to gen...@googlegroups.com
Hi,

I realise that there may be various ways of doing this, but I am trying to find the best way. I have also read the post https://groups.google.com/forum/#!topic/genson/joy2CdXyZ0w which covers this topic. There is a slight difference in what I want to do though and I am not quite sure how to tackle it.

Basically, I have a jersey based REST interface which uses genson to serialize and deserialize json/objects. Because some of the objects are quite large I want to be able to filter which fieds get serialized and which ones do not. Most of the fields are filtered out on the db-level, but some object still contains default values. I would therefore like to get genson to ignore  these and not serialize them. So the idea is that a user can filter the fields he wants: /some/api/object?fields=a,b,c.

The options I see are:

1) Use the Builder to set FieldFilter
2) Use include/exclude in Builder
3) Use BeanViewConverter
4) Use a custom BeanMutatorAccessorResolver

The problem I have with the FieldFilter and the include/exclude mechanism is that I would have to create a new Genson object every time - or do I not? The BeanViewConverter I cannot really use as it expects specific getters and setters to exist. I need to do this more generically as it could be any object and any field being filtered.
That leaves me with the BeanMutatorAccessorResolver. I already have one, so would it be enough just to extend it to return Trilean.FALSE if the field is to be ignored?

Any help would be greatly appreciated!

Thanks,
Michael


Eugen Cepoi

unread,
Mar 8, 2014, 5:46:46 AM3/8/14
to gen...@googlegroups.com
Hi Michael,

Hum you have quite an advanced use case. There is a easy solution using ContextualFactory (but maybe not optimal), this would allow you to filter at runtime fields you want to serialize, but it works only with POJOs (you can't filter map values based on keys).

If this solution does not solve your problem, there are other ways to do it, but more complex.

The idea is:

 1) You implement some code that will get the parameters from the query string and stores it in a ThreadLocal or elsewhere
 2) You implement a custom converter that will have the name of the field for which he is used + will check if it is present in the ThreadLocal Set.
   - If present it will serialize it by delegating to default ser
   - else does nothing

 3) Implement a ContextualFactory that will be called for each property of a bean when the converter chain is being built. There you will create your custom Converter.

The code looks like:


Genson genson = new Genson.Builder().withContextualFactory(new ContextualFactory<Object> {

        @Override
        public Converter<Object> create(BeanProperty property, Genson genson) {
            return new RuntimePropertyFilter(property);
        }
    }).create();


public class RuntimePropertyFilter implements Converter<Object> {
    private final BeanProperty property;


    @Override
    public void serialize(Object object, ObjectWriter writer, Context ctx) throws TransformationException, IOException {

        if (fieldsToKeep.get().contains(property.getName())) {
            ctx.genson.serialize(object, property.getType(), writer, ctx);
        } else {
            // do nothing
        }
    }

    @Override
    public Object deserialize(ObjectReader reader, Context ctx) throws TransformationException, IOException {
        if (fieldsToKeep.get().contains(property.getName())) {
            return ctx.genson.deserialize(GenericType.of(property.getType()), reader, ctx);
        } else {
            // consume if it is contained
            reader.skipValue();
            return null;
        }
    }

}


--
You received this message because you are subscribed to the Google Groups "Genson user group" group.
To unsubscribe from this group and stop receiving emails from it, send an email to genson+un...@googlegroups.com.
To post to this group, send email to gen...@googlegroups.com.
Visit this group at http://groups.google.com/group/genson.
To view this discussion on the web visit https://groups.google.com/d/msgid/genson/64cc2811-34d2-4352-bf24-232551ee1990%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

mdd...@gmail.com

unread,
Mar 8, 2014, 12:24:18 PM3/8/14
to gen...@googlegroups.com
Hi Eugen,

wow, wow, this works perfectly and is just what I was after :-). The pluggable architecture of genson ist perfect. Nice work!

Your solution works practically out of the box, but just incase it helps someone else, I'll post my code here too:

public class IgnoreFieldContextualFactory implements ContextualFactory<Object>

{
   
@Override
   
public Converter<Object> create(BeanProperty property, Genson genson)
   
{
       
return new RuntimePropertyFilter(property);
   
}
}

public class RuntimePropertyFilter implements Converter<Object>
{
   
private final BeanProperty property;


   
public RuntimePropertyFilter(BeanProperty property)
   
{
       
this.property = property;

   
}

   
@Override
   
public void serialize(Object object, ObjectWriter writer, Context ctx) throws TransformationException, IOException
   
{

       
QueryOptions queryOptions = A.PP.registryGet(FilterInjectableProvider.QUERY_OPTIONS_KEY);

       
if (queryOptions != null)
       
{
           
List<String> includeFields = queryOptions.getFieldsToInclude();
           
List<String> excludeFields = queryOptions.getFieldsToExclude();

           
// Only serialize these fields if they have been specified in the include-list.
           
if (includeFields != null && includeFields.size() > 0)
           
{
               
if (includeFields.contains(property.getName()))
               
{
                    ctx
.genson.serialize(object, property.getType(), writer, ctx);
               
}
           
}
           
// Otherwise, if an exclude-list exists, only serialize properties that are not in this list.
           
else if (excludeFields != null && excludeFields.size() > 0)
           
{
               
if (!excludeFields.contains(property.getName()))

               
{
                    ctx
.genson.serialize(object, property.getType(), writer, ctx);
               
}
           
}
       
}
       
else
       
{

           
// Default handling if no filtered fields have been specified.
            ctx
.genson.serialize(object, property.getType(), writer, ctx);

       
}
   
}

   
@Override
   
public Object deserialize(ObjectReader reader, Context ctx) throws TransformationException, IOException
   
{

       
// Default handling here.
       
return ctx.genson.deserialize(GenericType.of(property.getType()), reader, ctx);
   
}
}


    public static final Genson genson()
   
{
       
BeanMutatorAccessorResolver resolver = new BeanMutatorAccessorResolver.CompositeResolver(Arrays.asList(
           
new BeanMutatorAccessorResolver.GensonAnnotationsResolver(),
           
new SetterResolver(),
           
new BeanMutatorAccessorResolver.StandardMutaAccessorResolver()));

       
Genson genson = new Genson.Builder()
           
.setSkipNull(true)
           
.setWithClassMetadata(false)
           
.setWithDebugInfoPropertyNameResolver(true)
           
.setWithBeanViewConverter(true)
           
.withConverters(new IdConverter(), new UpdateConverter())
           
.withConverterFactory(ContextObjectConverter.factoryInstance)
           
.withContextualFactory(new IgnoreFieldContextualFactory()) // <--------- REGISERED HERE
           
.set(resolver)
           
.create();

       
return genson;
   
}


I used a ThreadLocal registry here in order to get the fields passed into the REST-GET-Method.

Thanks again and best regards,
Michael

Eugen Cepoi

unread,
Mar 8, 2014, 12:50:09 PM3/8/14
to gen...@googlegroups.com
Great, glad to see it works well with your advanced use cases :)


Eugen


Message has been deleted

Eugen Cepoi

unread,
Nov 3, 2014, 1:57:38 PM11/3/14
to gen...@googlegroups.com
Can you please provide an example of code to reproduce it (the json & the pojo definition).

2014-11-03 17:21 GMT+01:00 Daren Iott <dare...@gmail.com>:
I am using a derivation of this code but it does not call serialize method of the propertyfilter:


@Target({ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface SerializationProperty
{
/**
The name of that property.
*/
String value() default "";

/**
Whether this property must be serialized. Default is true, the property will be serialized.
*/
boolean serialize() default true;

/**
Whether this property must be deserialized. Default is true, the property will be
deserialized.
*/
boolean deserialize() default true;
}

public class Serialization
{
private static Serialization _instance;
private final Genson jsonLib;

private Serialization()
{
jsonLib = new Genson.Builder()
.setSkipNull(true)
.withContextualFactory(new SpecialFieldContextualFactory())
.create();
}

public static Serialization Instance()
{
if(_instance == null)
{
_instance = new Serialization();
}

return _instance;
}

public String Serialize(Object object)
{
String json = jsonLib.serialize(object);
return json;
}

public <T> T Deserialize(String jsonString, Class<T> type)
{
T object = jsonLib.deserialize(jsonString, type);
return object;
}

private class SpecialFieldContextualFactory implements ContextualFactory<Object>
{
@Override
public Converter<Object> create(BeanProperty property, Genson genson)
{
return new RuntimePropertyFilter(property);
}
}

private class RuntimePropertyFilter implements Converter<Object>
{
private final BeanProperty property;

public RuntimePropertyFilter(BeanProperty property)
{
this.property = property;

}

@Override
public void serialize(Object object, ObjectWriter writer, Context ctx) throws Exception
{
SerializationProperty annotation1 = property.getAnnotation(SerializationProperty.class);
if(annotation1 != null && !annotation1.serialize())
{
return;
}

ctx.genson.serialize(object, property.getType(), writer, ctx);
}

@Override
public Object deserialize(ObjectReader reader, Context ctx) throws Exception
{
SerializationProperty annotation1 = property.getAnnotation(SerializationProperty.class);
if(annotation1 != null && !annotation1.deserialize())
{
return null;
}

return ctx.genson.deserialize(GenericType.of(property.getType()), reader, ctx);
Reply all
Reply to author
Forward
0 new messages