Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

jsonParser.getParsingContext() stopped resolving path-ref and current location when upgrading from version 2.12.6 -> 2.13.3 #3534

Closed
ivangsa opened this issue Jul 3, 2022 · 2 comments

Comments

@ivangsa
Copy link

ivangsa commented Jul 3, 2022

Describe the bug
jsonParser.getParsingContext() has stopped resolving path-ref and current location when upgrading from version 2.12.6 -> 2.13.3

I'm trying create a map of json-paths to file line locations, very much like https://github.com/stoplightio/yaml

using a custom deserializer and the JsonParser Context:

https://github.com/ZenWave360/json-schema-ref-parser-jvm/blob/main/src/main/java/io/zenwave360/jsonrefparser/parser/JsonDeserializerWithLocations.java#L60

  • with jackson version 2.12.6 I can get path-name and current location
  • but with version 2.13.3 I don't get any meaningfull information

Version information
when upgrading from version 2.12.6 -> 2.13.3

To Reproduce
This is a minimal self-contained code to reproduce. Please note System.out code in line 8:

@Test
public void testUpgradeJacksonRegressionTest() throws IOException {
    ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
    var deserializer = new UntypedObjectDeserializer.Vanilla() {
        @Override
        public Object deserialize(JsonParser jsonParser, DeserializationContext ctxt) throws IOException {
            var context = jsonParser.getParsingContext();
            System.out.println("Context " + context.toString() + " - " + jsonParser.getCurrentLocation());
            return super.deserialize(jsonParser, ctxt);
        }
    };
    SimpleModule module = new SimpleModule();
    module.addDeserializer(Object.class, deserializer);
    mapper.registerModule(module);

    File file = new File("src/test/resources/asyncapi/shoping-cart-multiple-files/shoping-cart-multiple-files.yml");
    var result = mapper.readValue(file, Map.class);
}

Expected behavior

There is a huge difference in the output I get from System.out in line 8 between these two versions:

When run with version 2.12.6 this is the output I get from System.out in line 8 👇
Context {"asyncapi"} - [Source: (File); line: 1, column: 16]
Context {?} - [Source: (File); line: 3, column: 3]
Context {"title"} - [Source: (File); line: 3, column: 21]
Context {"version"} - [Source: (File); line: 4, column: 17]
Context {"description"} - [Source: (File); line: 7, column: 1]
Context {?} - [Source: (File); line: 8, column: 3]
Context {?} - [Source: (File); line: 9, column: 5]
Context {"url"} - [Source: (File); line: 9, column: 38]
Context {"description"} - [Source: (File); line: 10, column: 27]
Context {?} - [Source: (File); line: 12, column: 7]
Context {?} - [Source: (File); line: 13, column: 9]
Context [0] - [Source: (File); line: 14, column: 11]
Context [0] - [Source: (File); line: 14, column: 19]
Context [1] - [Source: (File); line: 15, column: 19]
Context {"default"} - [Source: (File); line: 16, column: 24]
Context {"protocol"} - [Source: (File); line: 17, column: 20]
Context {?} - [Source: (File); line: 19, column: 3]
Context {?} - [Source: (File); line: 20, column: 5]
Context {?} - [Source: (File); line: 21, column: 7]
Context {"$ref"} - [Source: (File); line: 21, column: 63]
Context {?} - [Source: (File); line: 23, column: 7]
Context {"$ref"} - [Source: (File); line: 23, column: 66]
Context {?} - [Source: (File); line: 25, column: 5]
Context {?} - [Source: (File); line: 26, column: 7]
Context {"$ref"} - [Source: (File); line: 26, column: 61]
Context {?} - [Source: (File); line: 29, column: 3]
Context {?} - [Source: (File); line: 30, column: 5]
Context [0] - [Source: (File); line: 31, column: 7]
Context [0] - [Source: (File); line: 31, column: 15]
Context {?} - [Source: (File); line: 33, column: 7]
Context {"operationId"} - [Source: (File); line: 33, column: 40]
Context {?} - [Source: (File); line: 35, column: 9]
Context [0] - [Source: (File); line: 36, column: 11]
Context {?} - [Source: (File); line: 36, column: 13]
Context {"$ref"} - [Source: (File); line: 36, column: 69]
Context {?} - [Source: (File); line: 37, column: 13]
Context {"$ref"} - [Source: (File); line: 37, column: 72]
Context {?} - [Source: (File); line: 39, column: 7]
Context {"operationId"} - [Source: (File); line: 39, column: 33]
Context {?} - [Source: (File); line: 41, column: 9]
Context {"$ref"} - [Source: (File); line: 41, column: 53]
  
When run with version 2.13.3 this is the output I get from System.out in line 8 👇
Context {"asyncapi"} - [Source: (File); line: 1, column: 16]
Context {?} - [Source: (File); line: 3, column: 3]
Context {?} - [Source: (File); line: 8, column: 3]
Context {?} - [Source: (File); line: 19, column: 3]
Context {?} - [Source: (File); line: 29, column: 3]
  

Additional context

My aproach to map object nodes to source lines worked ok with version 2.12 but feels hacky, maybe you have some pointer about how to implement this in a better way..

@ivangsa ivangsa added the to-evaluate Issue that has been received but not yet evaluated label Jul 3, 2022
@cowtowncoder
Copy link
Member

Ok, the problem here is the sub-classing of UntypedObjectDeserializer: this is unfortunately not supported usage.
In case of change, its internal implementation was changed to resolve a security issue (see #2816) and as a result deserialize() is not called recursively, leading to output change you see.
So while the change was obviously not meant to break existing code, it has been reported as problematic.
But it is not really possible to keep existing behavior wrt calls to deserialize() as that is the core part of the problem (wrt too deep recursion).

On deserializers: UntypedObjectDeserializer.Vanilla is meant as internal-only implementation; sub-classing of UntypedObjectDeserializer itself should be less problematic, although in general sub-classing of serializers/deserializers is not considered to be fully support public API wrt compatibility: we try our best not to break things but sub-classing of implementation types is by its nature more fragile than other types of reuse.
I have also changed Javadocs to try to reflect the specific expectations for UntypedObjectDeserializer, for what that is worth.

@cowtowncoder cowtowncoder removed the to-evaluate Issue that has been received but not yet evaluated label Jul 3, 2022
@ivangsa
Copy link
Author

ivangsa commented Jul 4, 2022

Ok, thanks
I remember extendeding UntypedObjectDeserializer.Vanilla because was the implementation I found while debuggin the desearialization for Map.class
Now subclassing UntypedObjectDeserializer it works again.
Thanks a lot!!

@ivangsa ivangsa closed this as completed Jul 4, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants