Jackson Mapper - String[] conversion possible?

This should be trivial, but I cannot figure out how to do it. In looking at custom annotations and additional registered serializers, I must be missing something. import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.ser.impl.StringArraySerializer; public class ExampleDto { @JsonSerialize(using = StringArraySerializer.class) public String[] field; } public class ExampleDtoTest { @Test public void testConvertToMap() { HashMap<String, Object> expectedMap = new HashMap<>() {{ put("field", (new String[]{"1","2","3"})); }}; ExampleDto exampleDto = new ExampleDto(); exampleDto.field = new String[]{"1","2","3"}; ObjectMapper mapper = new ObjectMapper(); HashMap result = mapper.convertValue(exampleDto, HashMap.class); assertEquals(String[].class, result.get("field").getClass()); // fails } }
21 Replies
JavaBot
JavaBot2mo ago
This post has been reserved for your question.
Hey @Jack9! Please use /close or the Close Post button above when your problem is solved. Please remember to follow the help guidelines. This post will be automatically marked as dormant after 300 minutes of inactivity.
TIP: Narrow down your issue to simple and precise questions to maximize the chance that others will reply in here.
tjoener
tjoener2mo ago
Use a List<String>. In Java 11 you can use List.of to make one
Kyo-chan
Kyo-chan2mo ago
Wait, what are you testing? Why do you care what convert(object, HashMap.cass) does?
Jack9
Jack9OP2mo ago
The test is to demonstrate the conversion to ArrayList. The question is how to convert a String[] property into a Hashmap<String, Object> with a <String, String[]> entry instead of the coerced <String, ArrayList<String>> entry. I believe what is happening is that the serializer is called to make json, which is then tokenized with a json parser. Then the jackson json parser is used to make the HashMap.class, which has a default jackson serializer. In order to ensure type safety of each element of an array, it uses an ArrayList. So the String[] type disappears. Making a ~50ln deserializer works to coerce the type back to String[] - but that seems overkill. Is there any other way? I would imagine this has come up before for Jackson.
Kyo-chan
Kyo-chan2mo ago
But... Jackson is for converting to JSON, not to arrays.
Jack9
Jack9OP2mo ago
[1,2,3] is simpler datatype that exists in JSON than ArrayList(){{ 1,2,3 }}. The question remains, other than a fairly robust deserializer to a mapper as a module, is there a simpler way?
Kyo-chan
Kyo-chan2mo ago
You just refuse to develop your thoughts regarding my observation?
Jack9
Jack9OP2mo ago
I don't understand.
Kyo-chan
Kyo-chan2mo ago
Jackson is for converting to JSON, not to arrays. What can possibly make it so you care whether that convert above will give out an array or a List?
Jack9
Jack9OP2mo ago
This isn't relevant. The question is about methodology, not circumstance. Why I care is that I am trying to figure out a way to do it, beyond my current solution. Let's just assume I can't control the source datastructure (it's part of another API in jar), does that matter? No. I have another API that needs a HashMap and it can only handle primitive members and primitive arrays.
Kyo-chan
Kyo-chan2mo ago
You do understand how much this is guaranteed to be an X/Y problem?
Jack9
Jack9OP2mo ago
@JsonSerialize addresses this same X/Y problem, from a different direction. I don't understand the avoidance.
Kyo-chan
Kyo-chan2mo ago
.... No. Jackson does the job it is expected to do. Whether internally arrays or lists are involved is none of the business of whoever isn't making Jackson itself.
Jack9
Jack9OP2mo ago
This is incorrect. Jackson provides a conversion API via convertValue. They implement a defaultSerializer which does not involve json
Kyo-chan
Kyo-chan2mo ago
That it provides that changes none of the previous observations.
Jack9
Jack9OP2mo ago
This was a long-winded way of saying "I don't know and don't care". GL with whatever.
Kyo-chan
Kyo-chan2mo ago
This was a long-winded way of confirming you're guaranteed to be in a X/Y problem situation, which experts on the domain may be uninterested in. But you do you.
Jack9
Jack9OP2mo ago
The following seems overkill, but it works (more or less, no float etc) added as: ObjectMapper mapper = new ObjectMapper(); SimpleModule module = new SimpleModule(); module.addDeserializer(HashMap.class, new CustomDeSerializer()); mapper.registerModule(module); HashMap<String,Object> result = mapper.convertValue(exampleDto, new TypeReference<>(){});
Jack9
Jack9OP2mo ago
If you can make due with the bytes as an Object[], rather than a String[] , mapper.configure(DeserializationFeature.USE_JAVA_ARRAY_FOR_JSON_ARRAY, true); There are a lot of discussions around a polymorphic deserializer here: https://github.com/FasterXML/jackson-databind/issues/1627 There was never a formal method. Lots of little pieces for subtypes and such. There's an approach of mapper.read out some json, then deserialize that with https://github.com/Pretius/pretius-jddl/ ObjectMapper mapper = new ObjectMapper(); MapType collectionType = mapper.getTypeFactory().constructMapType(HashMap.class, String.class, String[].class); HashMap<String,Object> result = mapper.convertValue(exampleDto, collectionType); This is an initialization that is needed to pass the test. It does not survive additional field types, but it does avoid the conversion to ArrayList succinctly.
JavaBot
JavaBot2mo ago
Before your post will be closed, would you like to express your gratitude to any of the people who helped you? When you're done, click I'm done here. Close this post!.
JavaBot
JavaBot2mo ago
Post Closed
This post has been closed by <@374014726348210176>.
Want results from more Discord servers?
Add your server