Hi, Thanks a lot for your response. Sorry if you were unable to follow my question (stackoverflow <https://stackoverflow.com/q/69052269/7584240>). I am sure the problem is happening because point 2 mentioned by you but I am not understanding how to find a workaround for it.
I will try to make it a bit simple so you can understand provide some workaround for me (I have posted this on Stackoverflow as well so the code and text are a bit formatted for easy understanding https://stackoverflow.com/q/69052269/7584240): I am using Jackson to deserialize a JSON. The input JSON can be of 2 types `CustomerDocument` or `Single Customer`. `CustomerDocument` will have a `CustomerList` which can consist of a huge number of `Customer` and the `Single Customer` will have just a `Single Customer`. Hence, the Jackson has to handle 2 things: 1. Identify if the provided JSON is a `CustomerDocument`, if so then deserialize the elements in `CustomerList` one by one rather than storing the whole thing into `List` so as to reduce the memory usage. 2. Identify if the provided JSON is a `single Customer` and if so then deserialize the `customer` directly. I am able to achieve this and everything is working as expected but when I provide the `CustomerDocument` then it's unable to read the `@Context` key-value pair as it has been already read during the check for single `Customer` (as mentioned by you in point 2). I guess the below code would make the problems clear: Following is the JSON I am trying to deserialize: ``` { "@context": [ "https://stackoverflow.com", { "example": "https://example.com" } ], "isA": "CustomerDocument", "customerList": [ { "isA": "Customer", "name": "Batman", "age": "2008" } ] } ``` Following is my Customer POJO class: ``` @Data @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, visible = true, property = "isA") @JsonInclude(JsonInclude.Include.NON_NULL) public class Customer implements BaseResponse { private String isA; private String name; private String age; } @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, visible = true, property = "isA") @JsonSubTypes({ @JsonSubTypes.Type(value = Customer.class, name = "Customer")}) interface BaseResponse { } ``` Following is my `Main` class which will read the `JSON InputStream` and make the decision whether the provided input JSON is `CustomerList` or `Single Customer` and then deserialize accordingly: ``` public class JacksonMain { public static void main(String[] args) throws IOException { final InputStream jsonStream = JacksonMain.class.getClassLoader().getResourceAsStream("Customer.json"); final JsonParser jsonParser = new JsonFactory().createParser(jsonStream); final ObjectMapper objectMapper = new ObjectMapper(); jsonParser.setCodec(objectMapper); //Goto the start of the document jsonParser.nextToken(); try { BaseResponse baseResponse = objectMapper.readValue(jsonParser, BaseResponse.class); System.out.println("SINGLE EVENT INPUT" + baseResponse.toString()); } catch (Exception e) { System.out.println("LIST OF CUSTOMER INPUT"); //Go until the customerList has been reached while (!jsonParser.getText().equals("customerList")) { System.out.println("Current Token Name : " + jsonParser.getCurrentName()); if (jsonParser.getCurrentName() != null && jsonParser.getCurrentName().equalsIgnoreCase("@context")) { System.out.println("WITHIN CONTEXT"); } jsonParser.nextToken(); } jsonParser.nextToken(); //Loop through each object within the customerList and deserilize them while (jsonParser.nextToken() != JsonToken.END_ARRAY) { final JsonNode customerNode = jsonParser.readValueAsTree(); final String eventType = customerNode.get("isA").asText(); Object event = objectMapper.treeToValue(customerNode, BaseResponse.class); System.out.println(event.toString()); } } } } ``` Following is the output I am getting: ``` LIST OF CUSTOMER INPUT Current Token Name : isA Customer(isA=Customer, name=Batman, age=2008) ``` As we can see it's printing only `Current Token Name: isA` I would expect it to print `isA` and `@Context` because it's present before the `isA`. But I am aware that it's not printing because it has already passed that due to the following line of code in `try` block: ``` BaseResponse baseResponse = objectMapper.readValue(jsonParser, BaseResponse.class); ``` Can someone please suggest to me how can I achieve this and is there a better workaround for this issue? Please note: 1. The `CustomerList` can have a lot of `Customers` hence I do not want to store the whole `CustomerList` into some `List` as it can take a lot of memories. Hence, I am using `JsonParser` so I can read one `JsonToken` at a time. 2. Also, I do not want to create a `CustomerList` class rather than that I want to read one `Customer` at a time and deserialize it. 3. The JSON structure cannot be modified as it's coming from another application and it's a standard format for my application. On Sunday, 5 September 2021 at 03:34:25 UTC+5:30 [email protected] wrote: > Unfortunately this example is quite long so I cannot quite understand the > intent; but I have 2 suggestions in trying to trace the behavior. > > 1. Instead of assuming current token type to be certain thing, verify what > the token is: some methods (like "parser.getText()" and > "parser.getCurrentName()") are applicable to different token types -- and > just because you have "current name" does not necessarily mean you have > FIELD_NAME token (in fact it is the value of last name for token following) > 2. When a parse exception is thrown, underlying JsonParser is NOT > guaranteed to be in a consistent state. Parser should still have > information about the last successfully decoded token (but not in case > where we are half-way through decoding a new one, and fail), and the > location where the problem is encountered. But it SHOULD NOT be assumed > that you can further parse content successfully. It is possible no content > is available; internal state may be corrupt and so forth. > > -+ Tatu +- > > > On Thu, Sep 2, 2021 at 11:44 PM Aravinda Baliga B <[email protected]> > wrote: > >> I am building a Jackson deserialization application that can handle the >> deserialization of `CustomerList` and `Customer`. Users can provide any >> input and based on the input the code will make the decision whether the >> provided input JSON is `CustomerList` or `Single Customer`. >> >> Everything is working as expected apart from one small thing. When I >> provide the `CustomerList` JSON then it would skip the first `key value` >> pair. In my case, it's skipping the `@Context`. >> >> Following is the `JSON` i am trying to deserialize: >> ``` >> { >> "@context": [ >> "https://stackoverflow.com", >> { >> "example": "https://example.com" >> } >> ], >> "isA": "CustomerDocument", >> "customerList": [ >> { >> "isA": "Customer", >> "name": "Batman", >> "age": "2008" >> } >> ] >> } >> ``` >> >> Following is my Customer POJO class: >> ``` >> @Data >> @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = >> JsonTypeInfo.As.PROPERTY, visible = true, property = "isA") >> @JsonInclude(JsonInclude.Include.NON_NULL) >> public class Customer implements BaseResponse { >> private String isA; >> private String name; >> private String age; >> } >> >> >> @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = >> JsonTypeInfo.As.PROPERTY, visible = true, property = "isA") >> @JsonSubTypes({ >> @JsonSubTypes.Type(value = Customer.class, name = "Customer")}) >> interface BaseResponse { >> } >> ``` >> >> Following is the Main: >> ``` >> public class JacksonMain { >> public static void main(String[] args) throws IOException { >> final InputStream jsonStream = >> JacksonMain.class.getClassLoader().getResourceAsStream("Customer.json"); >> final JsonParser jsonParser = new >> JsonFactory().createParser(jsonStream); >> final ObjectMapper objectMapper = new ObjectMapper(); >> jsonParser.setCodec(objectMapper); >> >> //Goto the start of the document >> jsonParser.nextToken(); >> >> try { >> BaseResponse baseResponse = >> objectMapper.readValue(jsonParser, BaseResponse.class); >> System.out.println("SINGLE EVENT INPUT" + >> baseResponse.toString()); >> } catch (Exception e) { >> System.out.println("LIST OF CUSTOMER INPUT"); >> //Go until the customerList has been reached >> while (!jsonParser.getText().equals("customerList")) { >> System.out.println("Current Token Name : " + >> jsonParser.getCurrentName()); >> if (jsonParser.getCurrentName() != null && >> jsonParser.getCurrentName().equalsIgnoreCase("@context")) { >> System.out.println("WITHIN CONTEXT"); >> } >> jsonParser.nextToken(); >> } >> jsonParser.nextToken(); >> >> //Loop through each object within the customerList and >> deserilize them >> while (jsonParser.nextToken() != JsonToken.END_ARRAY) { >> final JsonNode customerNode = >> jsonParser.readValueAsTree(); >> final String eventType = customerNode.get("isA").asText(); >> Object event = objectMapper.treeToValue(customerNode, >> BaseResponse.class); >> System.out.println(event.toString()); >> } >> } >> } >> } >> ``` >> >> When I run the application I get the following response: >> ``` >> LIST OF CUSTOMER INPUT >> Current Token Name : isA >> Customer(isA=Customer, name=Batman, age=2008) >> ``` >> >> As we can see it's printing only `Current Token Name: isA` I would expect >> it to print `isA and @Context` because it's present before the `isA`. >> >> The weird thing that I am seeing is that if I switch the places of `isA` >> and `@context` in my `JSON` something like this: >> ``` >> { >> "isA": "CustomerDocument", >> "@context": [ >> "https://stackoverflow.com", >> { >> "example": "https://example.com" >> } >> ], >> "customerList": [ >> { >> "isA": "Customer", >> "name": "Batman", >> "age": "2008" >> } >> ] >> } >> ``` >> >> Then I get the output like this, As you can see now it's reading the >> `@context` and `isA` both. >> ``` >> LIST OF CUSTOMER INPUT >> Current Token Name : isA >> Current Token Name : @context >> WITHIN CONTEXT >> Current Token Name : @context >> WITHIN CONTEXT >> Current Token Name : null >> Current Token Name : null >> Current Token Name : example >> Current Token Name : example >> Current Token Name : null >> Current Token Name : @context >> WITHIN CONTEXT >> Customer(isA=Customer, name=Batman, age=2008) >> ``` >> >> Since it's working in the second case I am thinking it's not an issue >> with my code. But I am not sure what's causing the issue for it to not read >> in the `firstcase` (with `@context` first and `isA` second). Can someone >> please help me understand this issue and provide some workaround as I am >> trying since yesterday to figure out the problem? Is this a bug in Jackson? >> >> Please note: >> The JSON is out of my control as it's coming from another application. >> Provided JSON is a replica of my actual application JSON. >> >> The second JSON that I can feed to the application is the direct customer >> rather than a list: (with the current code this is working perfectly) >> ``` >> { >> "@context": [ >> "https://stackoverflow.com", >> { >> "example": "https://example.com" >> } >> ], >> "isA": "Customer", >> "name": "Batman", >> "age": "2008" >> } >> >> ``` >> >> Can someone please help me understand why Jackson is skipping the >> `@context` when provided first and provide some workaround as I am trying >> since yesterday to figure out the problem? Is this a bug in Jackson? >> >> -- >> 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 view this discussion on the web visit >> https://groups.google.com/d/msgid/jackson-user/8b99e386-325a-45a7-9616-74ec142b45c4n%40googlegroups.com >> >> <https://groups.google.com/d/msgid/jackson-user/8b99e386-325a-45a7-9616-74ec142b45c4n%40googlegroups.com?utm_medium=email&utm_source=footer> >> . >> > -- 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 view this discussion on the web visit https://groups.google.com/d/msgid/jackson-user/d8790fc3-3aec-4b42-8662-f4895dc568acn%40googlegroups.com.
