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

ArrayListMultimapDeserializer does not support multimaps inside another object as a property #104

Closed
magical-l opened this issue Feb 25, 2023 · 13 comments
Labels
Milestone

Comments

@magical-l
Copy link
Contributor

CN: (guava module) ArrayListMultimapDeserializer 未支持multimap作为其他对象的属性。
EN: (guava module) ArrayListMultimapDeserializer does not support multimaps inside another object as a property

CN: 在 com.fasterxml.jackson.datatype.guava.deser.multimap.GuavaMultimapDeserializer:209 行(deserializeFromSingleValue(JsonParser p, DeserializationContext ctxt) 方法中),我们将会检查p.getCurrentToken()是否为JsonToken.START_OBJECT
EN: at com.fasterxml.jackson.datatype.guava.deser.multimap.GuavaMultimapDeserializer:209 (inside the method deserializeFromSingleValue(JsonParser p, DeserializationContext ctxt) ), we will check whether the value of p.getCurrentToken() is JsonToken.START_OBJECT.

若multimap作为json的整体,此时p.getCurrentToken()确实是JsonToken.START_OBJECT。
if we are deserialize the whole json object to a multimap, then p.getCurrentToken() is exactly JsonToken.START_OBJECT.

但若multimap作为另一个对象的属性,如下:
but what if the multimap is a property of another object:

{
    'theMultimapProperty':{
        '@class':'com.google.common.collect.ArrayListMultimap',//or com.google.common.collect.Multimaps$CustomListMultimap ...
        'a': [1,2]
    }
}

此时p.getCurrentToken()将是JsonToken.FIELD_NAME,对应其作为另一对象属性的属性名theMultimapProperty
then now the value of p.getCurrentToken() would be JsonToken.FIELD_NAME , as the property name for the multimap in the outside object theMultimapProperty.

然后解析失败。
and then the deserialization fails.

jackson version: 2.13.4

@magical-l
Copy link
Contributor Author

oh, should move to the jackson-datatypes-collections repo.

@cowtowncoder cowtowncoder transferred this issue from FasterXML/jackson-databind Feb 26, 2023
@cowtowncoder cowtowncoder added guava need-test-case To work on issue, a reproduction (ideally unit test) needed labels Feb 26, 2023
@cowtowncoder
Copy link
Member

It sounds like there may be a problem to solve. But we would need a full reproduction: looks like this might be using polymorphic deserialization, for example. Description alone is not necessarily sufficient to see how to trigger the problem.

@magical-l
Copy link
Contributor Author

It sounds like there may be a problem to solve. But we would need a full reproduction: looks like this might be using polymorphic deserialization, for example. Description alone is not necessarily sufficient to see how to trigger the problem.

em, here is an example:

static class Outside {
		public Object aProperty;
	}

	public static void main(final String... args) throws JsonProcessingException, JsonMappingException {
		final var mapper = JsonMapper.builder().addModule(new GuavaModule())//
			.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, DefaultTyping.NON_FINAL, As.PROPERTY)//
			.build();
		final var multimap = ArrayListMultimap.create();
		multimap.put("aKey", 1);
		final var outside = new Outside();
		outside.aProperty = multimap;
		final var s = mapper.writeValueAsString(outside);
		System.out.println(s);
		final var r = mapper.readValue(s, Outside.class);
		System.out.println(r);
	}

it will throw a JsonMappingException at com.fasterxml.jackson.datatype.guava.deser.multimap.GuavaMultimapDeserializer#expect:269.

@cowtowncoder
Copy link
Member

Ok thank you @magical-l .

@cowtowncoder cowtowncoder removed the need-test-case To work on issue, a reproduction (ideally unit test) needed label Mar 1, 2023
@cowtowncoder cowtowncoder changed the title (guava module) ArrayListMultimapDeserializer does not support multimaps inside another object as a property ArrayListMultimapDeserializer does not support multimaps inside another object as a property Mar 1, 2023
@cowtowncoder
Copy link
Member

cowtowncoder commented Mar 1, 2023

@magical-l Did you actually test this against later versions? I cannot reproduce it with 2.14.2. This is pretty much the first thing one should do before reporting bugs. :-(

@magical-l
Copy link
Contributor Author

@cowtowncoder
oh! my version is 2.13.4, after I update to 2.14.2, it's passed. it has been fixed! that's great! and sorry about bothering...

@magical-l
Copy link
Contributor Author

@cowtowncoder
so there is a new but relevant problem now... when the multimap is empty,

	static class Outside {
		public Object aProperty;
	}

	public static void main(final String... args) throws JsonProcessingException, JsonMappingException {
		final var mapper = JsonMapper.builder().addModule(new GuavaModule())//
			.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, DefaultTyping.NON_FINAL, As.PROPERTY)//
			.build();
		final var multimap = ArrayListMultimap.create();
//		multimap.put("aKey", 1);  //now we use an empty multimap
		final var outside = new Outside();
		outside.aProperty = multimap;
		final var s = mapper.writeValueAsString(outside);
		System.out.println(s);
		final var r = mapper.readValue(s, Outside.class);
		System.out.println(r);
	}

then the exception will happen at the same place(expect method):

{"@class":"......$Outside","aProperty":{"@class":"com.google.common.collect.ArrayListMultimap"}}
Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: Expecting START_OBJECT to start `MultiMap` value, found END_OBJECT
 at [Source: (String)"{"@class":"com.lanmudata.super4s.indicatorservice.api.support.O2$Outside","aProperty":{"@class":"com.google.common.collect.ArrayListMultimap"}}"; line: 1, column: 143] (through reference chain: com.lanmudata.super4s.indicatorservice.api.support.O2$Outside["aProperty"])
	at com.fasterxml.jackson.datatype.guava.deser.multimap.GuavaMultimapDeserializer.expect(GuavaMultimapDeserializer.java:274)

@cowtowncoder
Copy link
Member

Ok. I'll have a look :)

@cowtowncoder
Copy link
Member

Ok, yes, I can reproduce the issue. I was able to add a failing test; hoping to look into what gives tomorrow.

@cowtowncoder
Copy link
Member

Ok this is weird: serialization of polymorphic value seems completely wrong... type id should not be written as property as that won't work with Maps, in general. But code itself looks fine, similar to what MapSerializer does.

@cowtowncoder
Copy link
Member

One work-around, fwtw: use As.WRAPPER_ARRAY or As.WRAPPER_OBJECT instead of As.PROPERTY.
Those work.

@cowtowncoder
Copy link
Member

Ok. Think I found the fix... and also learned something about polymorphic serialization of Maps: they do get serialized as JSON Objects still, which is interesting as Type Id is among actual values. Didn't realize this is what happens.

@cowtowncoder cowtowncoder modified the milestones: 2.14., 2.14.3 Mar 2, 2023
@cowtowncoder
Copy link
Member

Fixed in 2.14 for 2.14.3; now empty Multimaps can be written, read as polymorphic values too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants