On Tue, Feb 12, 2019 at 8:43 PM John Passaro <[email protected]> wrote:
>
> On Tue, Feb 12, 2019, 23:02 Tatu Saloranta <[email protected] wrote:
>>
>> On Tue, Feb 12, 2019 at 1:18 PM John Passaro <[email protected]> 
>> wrote:
>> >
>> > Hello folks! I am trying to deserialize a yaml file with anchors and 
>> > references. There are some existing StackOverflow questions along these 
>> > lines but the answers aren't quite getting me to the finish line.
>> >
>> > Here is my model:
>> >
>> > @JsonIdentityInfo(generator = ObjectIdGenerators.None.class)
>> > class YamlScratch {
>> >     @JsonProperty("misc")
>> >     List<List<String>> misc;
>> >
>> >     @JsonProperty("contents")
>> >     Map<String, Config> contents;
>> >
>> >     static class Config {
>> >         @JsonProperty("header")
>> >         String header;
>> >
>> >         @JsonProperty("labels")
>> >         @JsonIdentityInfo(generator = ObjectIdGenerators.None.class)
>> >         List<String> labels;
>> >     }
>> > }
>> >
>> >
>> >
>> > Here is my YAML:
>> >
>> > misc:
>> >   - &letters
>> >     - Aie
>> >     - Bee
>> >     - See
>> >   - &numbers
>> >     - One
>> >     - Two
>> >     - Three
>> >
>> > contents:
>> >   letters:
>> >     header: "This is a list of phonetic letters"
>> >     labels: *letters
>> >   numbers:
>> >     header: "This is a list of number spellings"
>> >     labels: *numbers
>> >   moreletters:
>> >     header: "this is another copy of the letters"
>> >     labels: *letters
>> >
>> >
>> > The idea is that I have lists that may be referenced more than once in the 
>> > "contents" tree, and I'd like to be able to reference them concisely.
>> >
>> > I've tried to enable this in my Java code by adding @JsonIdentityInfo to 
>> > the "labels" field that will reference these lists. As for the YAML, I 
>> > added "&..." anchors to the data that will be referenced, and where it 
>> > should appear I added "*..." references. PyYAML seems to confirm this is 
>> > the correct YAML usage:
>> >
>> > $ python3
>> > >>> import yaml
>> > >>> f = 
>> > >>> open("/Users/johnpassaro/Library/Preferences/IdeaIC2018.3/scratches/scratch.yml")
>> > >>> y = yaml.load(f)
>> > >>> y
>> > {'misc': [['Aie', 'Bee', 'See'], ['One', 'Two', 'Three']], 'contents': 
>> > {'letters': {'header': 'This is a list of phonetic letters', 'labels': 
>> > ['Aie', 'Bee', 'See']}, 'numbers': {'header': 'This is a list of number 
>> > spellings', 'labels': ['One', 'Two', 'Three']}}}
>> >
>> >
>> > With Jackson (2.9), I get an error:
>> >
>> > Exception in thread "main" 
>> > com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot 
>> > deserialize instance of java.util.ArrayList out of VALUE_STRING token
>> >  at [Source: (File); line: 14, column: 13] (through reference chain: 
>> > YamlScratch["contents"]->java.util.LinkedHashMap["letters"]->YamlScratch$Config["labels"])
>> >
>> > I tried changing
>> > List<String> labels;
>> > to
>> > Object labels;
>> > just to see how Jackson was treating it: instead of resolving the anchor 
>> > reference ("*letters"), it just returns the reference name as as a string 
>> > ("letters").
>> >
>> > One of the StackOverflow questions has an answer that mentions the feature 
>> > YAMLParser.Feature.USE_NATIVE_OBJECT_ID. That would seem to be exactly 
>> > what I need, but that enum value is not present in 2.9.
>> >
>> > Is this behavior supported at all? If so, what do I need to do to make it 
>> > work?
>> >
>> > Many thanks in advance for your help. I'd be happy to post the results on 
>> > the relevant SO threads to make sure this information get shared 
>> > reasonably widely.
>>
>> Ok. I think I can point to the problem itself at least.
>>
>> For Jackson to handle anchors and references, property value types (or
>> property declarations) need `@JsonIdentityInfo`; otherwise Jackson
>> does not know to look for, or keep track of, anchors (ids for values
>> to reference) or references.
>>
>> But one limitation is that only POJO types support Object Id handling,
>> and here references would be to Lists of Strings (or maybe Lists of
>> Lists). In theory it would be possible to handle Object Ids for
>> Collection, Map and array types, but they are not supported at this
>> point.
>>
>> It might, however, be possible for you to create POJO type that gets
>> serialized as yaml/json Array, just like List (and deserialized from
>> as well). This could work by using combination of `@JsonValue` (to get
>> `List` to serialize) and `@JsonCreator` annotated constructor that
>> takes actual `List` value you want; or, using Converters. Either way
>> once you get that working, and type itself annotated with
>> `@JsonIdentityInfo` it should work.
>>
>> I know this is sub-optimal and leaves out some valid YAML cases. But
>> it just might work for your usage.
>>
>> I hope this helps,
>>
>> -+ Tatu +-
>
>
> I will certainly try this out, it does seem like it would address my need. 
> Thank you for the close attention to the example and for your suggestion. If 
> it works I'll post my findings (i.e. working example) here and maybe add to 
> those SO questions.
>
> It seems to me there might be a friendlier experience if there was a 
> YamlParser.Feature to treat the entire document as having the 
> JsonIdentityInfo annotation, including Lists and Maps and their contents - 
> that is, resolving anchor references everywhere without the need for 
> indicating where the user expects them to be. I don't know enough to assert 
> that this is closer to the intention of the anchor/reference feature or of an 
> average document that uses it, but it is closer to how PyYAML treats them, 
> which I've found very useful. I hope you'll consider supporting such usage.
>
> Regardless, thank you for your help and for your work on this powerful 
> library.

Yes, it is true that for YAML documents the idea of using "default
identity" (similar to "default typing" for polymorphic type handling)
would be useful.
Unfortunately I am not sure how practical it would be for general
case, one problem being the limited support for non-POJO types.

In practice one could achieve something like this by custom
`AnnotationIntrospector` that basically "finds" equivalent of
`@JsonIdentityInfo` on every introspected type.

But as to more general handling... I wonder if `JsonNode` could be
forced (with a feature or YAMLMapper setting) to support anchors/refs
and in THAT case you could first resolve all of these and THEN map to
actual POJOs.

I will file an issue for that -- I can't promise I can make that
happen, but I think that is a reasonable idea and could help tackle
this completely without requiring annotations.

-+ 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 [email protected].
To post to this group, send email to [email protected].
For more options, visit https://groups.google.com/d/optout.

Reply via email to