Dear Tatu,
I really thank you for your immediate and very thorough answer, I really 
appreciate!

I tried with the SerializerProvider approach but I was not able to 
implement something that works. Could you please point me to the right 
direction with the approach no.(2)? Is there any simple example I can start 
with in the jackson codebase?

Thank you!

matteo

On Wednesday, August 9, 2017 at 9:44:59 PM UTC+2, Tatu Saloranta wrote:
>
> On Tue, Aug 8, 2017 at 4:03 PM, Matteo <matteo...@gmail.com <javascript:>> 
> wrote: 
> > Hello! 
> > 
> > I'm trying to write a custom serializer that can transform the following 
> > structure 
> > 
> > { 
> >     "a": { 
> >         "@context": "context-a", 
> >         "aKey": "aValue" 
> >     }, 
> >     "b": { 
> >         "@context": "context-b", 
> >         "anotherbKey": "anotherbValue", 
> >         "bKey": "bValue" 
> >     } 
> > } 
> > 
> > into this: 
> > 
> > { 
> >     "@context" : ["context-a", "context-b"], 
> >     "a": { 
> >         "aKey": "aValue" 
> >     }, 
> >     "b": { 
> >         "anotherbKey": "anotherbValue", 
> >         "bKey": "bValue" 
> >     } 
> > } 
> > 
> > The reason to do this is to put all json-ld headers at the beginning of 
> the 
> > serialized json. I have a utility class that extract all @context 
> attribute 
> > from a bean hierarchy (ContextsCrawler in the snippet below) and my 
> current 
> > serializer attempt is this: 
> > 
> > public class JsonLdModelSerializer extends JsonSerializer<Object> { 
> > 
> >     private static Optional<String> baseContext; 
> >     private static ContextsCrawler ctxCrawler = new ContextsCrawler(); 
> > 
> >     public static void scanClassForContexts(Map<Class<?>, Class<?>> 
> mixins) 
> > { 
> >         ctxCrawler.scanClassForContexts(mixins); 
> >     } 
> > 
> >     public static void setBaseContext(String baseContext) { 
> >         JsonLdModelSerializer.baseContext = Optional.of(baseContext); 
> >     } 
> > 
> >     @Override 
> >     public void serialize(Object value, JsonGenerator jgen, 
> > SerializerProvider serializers) 
> >             throws IOException, JsonProcessingException { 
> > 
> >         jgen.writeStartObject(); 
> >         // Add contexts elements in top level element only: 
> >         if (jgen.getOutputContext().getParent().inRoot()) { 
> >             Collection<String> cxts = ctxCrawler.getContexts(value); 
> >             if (cxts != null) { 
> >                 Set<String> ctxset = new HashSet<>(); 
> >                 baseContext.ifPresent(ctx -> ctxset.add(ctx)); 
> >                 ctxset.addAll(cxts); 
> >                 jgen.writeObjectField("@context", ctxset); 
> >             } 
> >         } 
> >         JavaType javaType = serializers.constructType(value.getClass()); 
> >         BeanDescription beanDesc = 
> > serializers.getConfig().introspect(javaType); 
> >         JsonSerializer<Object> serializer = 
> > BeanSerializerFactory.instance.findBeanSerializer(serializers, 
> >                 javaType, 
> >                 beanDesc); 
> >         serializer.unwrappingSerializer(null).serialize(value, jgen, 
> > serializers); 
> > 
> >         jgen.writeEndObject(); 
> >     } 
> > } 
> > 
> > 
> > Then I simply define a mixin for each class that contains a @context 
> element 
> > like this: 
> > 
> > @JsonSerialize(using = JsonLdModelSerializer.class) 
> > @JsonIgnoreProperties("context") 
> > public class CapabilityMixIn extends Capability { 
> >  @Override 
> >  @JsonLdContextProvider // used by ContextsCrawler 
> >  public List<String> getContext() { 
> >   return super.getContext(); 
> >  } 
> > } 
> > 
> > It works but it has two main problems: 
> > 
> > 1. I don't know it the direct usage of BeanSerializerFactory in 
> serialize 
> > method is OK since I read that this is not the best approach to use. I'm 
> > also worried about performance implications; 
> > 2. For some reason this serializer doesn't play nice with beans that 
> > contains Map<String,Object> that are decorated with @JsonAnyGetter: the 
> > resulting json does not contain the map elements 
> > 
> > Could you please provide guidance? What is the right approach to 
> implement 
> > such a JsonLdModelSerializer? 
>
> Ok, that is quite an elaborate setup. 
>
> So... typically I recommend multi-phase processing for most structural 
> changes: first "serialize" content into `JsonNode` using 
> `ObjectMapper.valueToTree()`; then transform tree, and finally 
> serialize the modified tree as JSON. 
> This is especially true for cases where you need to apply 
> transformations for all kinds of types, not just specific classes. 
> Serializer/deserializer setup is designed more for strict(er) typing, 
> and as such is not necessarily good at applying things for all types. 
>
> But it is certainly possible to do this via serializers too. Usually I 
> would have looked at `StdDelegatingSerializer`, in which you can take 
> a specific type (that Jackson does not know how to properly 
> serialize), and construct alternative value (like `JsonNode` or `Map`) 
> and let Jackson serialize that instead. This is somewhat similar to 
> use of `@JsonValue` annotation, in which POJO is serialized using sort 
> of surrogate. 
> But I am not sure this approach would work here, since this is applied 
> to any types it seems (`Object`), and since you may want to operate on 
> outputs of standard Bean-style serialization. 
>
> As to use of `BeanSerializerFactory`: you are correct in suspecting 
> this is not the way to do it. It is not. :-) 
>
> Instead you would ideally do one of: 
>
> 1. If types are statically known, implement `ContextualSerializer`, in 
> which you can get access to the "standard" serializer, keep reference 
> to it, add your own implementation that may call original one 
> (including giving it `TokenBuffer` as `JsonGenerator`!). 
> 2. If types not known until actual serialization (dynamically) -- 
> which I think is what you have here -- find serializer(s) via 
> `SerializerProvider`. It will call factory, as well as possible 
> extension modules, to find and initialize serializer. 
>
> I can help with (2) if you have hard time finding method to call; for 
> now I assume you can find it ok. 
>
> I hope this helps, 
>
> -+ Tatu +- 
>

-- 
You received this message because you are subscribed to the Google Groups 
"jackson-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to jackson-user+unsubscr...@googlegroups.com.
To post to this group, send email to jackson-user@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to