On Thu, Aug 10, 2017 at 2:00 PM, Matteo <matteo.ru...@gmail.com> wrote:
> 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?

Most serializers resolve dependant types in `createContextual()`, but
method to call is typically
`findValueSerializer()`. If possible, you would still define
`createContextual()` to get hold of `BeanProperty` in question (which
may or may not have annotations that might affect behavior of
serializer), to pass that. If that is of no consequence however can
use variant that only takes `Class<?>` (or `JavaType`, if needed for
generic value type).

Other variants (`findTypeValueSerializer()`) are relevant for
polymorphic handling.

Regardless of which method you call, use of resulting serializer
should be the same as in your original example.

-+ Tatu +-

>
> 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> 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.

-- 
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