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

Cannot parse map if the map contains 0 #15

Open
MrPowerGamerBR opened this issue May 3, 2019 · 2 comments
Open

Cannot parse map if the map contains 0 #15

MrPowerGamerBR opened this issue May 3, 2019 · 2 comments

Comments

@MrPowerGamerBR
Copy link

MrPowerGamerBR commented May 3, 2019

val parsedMap = Constants.HOCON_MAPPER.readValue<Map<Int, String>>("""{ 1 = "abc" }""")
println(parsedMap)

Output: {1=abc}

val parsedMap = Constants.HOCON_MAPPER.readValue<Map<Int, String>>("""{ 0 = "abc" }""")
println(parsedMap)

Output:

Exception in thread "main" com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.util.LinkedHashMap` out of START_ARRAY token
 at [Source: UNKNOWN; line: -1, column: -1]
	at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:63)
	at com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1343)
	at com.fasterxml.jackson.databind.DeserializationContext.handleUnexpectedToken(DeserializationContext.java:1139)
	at com.fasterxml.jackson.databind.DeserializationContext.handleUnexpectedToken(DeserializationContext.java:1093)
	at com.fasterxml.jackson.databind.deser.std.StdDeserializer._deserializeFromEmpty(StdDeserializer.java:600)
	at com.fasterxml.jackson.databind.deser.std.MapDeserializer.deserialize(MapDeserializer.java:360)
	at com.fasterxml.jackson.databind.deser.std.MapDeserializer.deserialize(MapDeserializer.java:29)
	at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4013)
	at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3023)
	at net.perfectdreams.loritta.QuirkyStuffKt.main(QuirkyStuff.kt:63)
	at net.perfectdreams.loritta.QuirkyStuffKt.main(QuirkyStuff.kt)

This happens even if the Map is another type (Example: Map<String, String>)

This does NOT happen when parsing with lightbend/config:

val conf = ConfigFactory.parseString("""{ 0 = "abc" }""")
println(conf)

Output: Config(SimpleConfigObject({"0":"abc"}))

@MrPowerGamerBR
Copy link
Author

I guess that this isn't possible to fix, this happens due to type loss, so the parser needs to do heuristics to figure out if it is a map or a array.

The problem happens when you try to use 0 on a map, because it matches the heuristics, it thinks it is a numerically indexed array... causing issues.

@MrPowerGamerBR
Copy link
Author

MrPowerGamerBR commented May 4, 2019

It looks like @jclawson is inactive, so I guess this bug won't ever be fixed.

In the meantime, I made a custom deserializer with a hacky fix for this issue.

class FixedMapDeserializer : JsonDeserializer<Map<*, *>>() {
    override fun deserialize(p0: JsonParser, p1: DeserializationContext): Map<*, *> {
        val any = p0.readValueAsTree<TreeNode>()
        if (any.isArray) {
            val values = mutableMapOf<Any?, Any?>()
            repeat(any.size()) {
                values[it] = any.get(it)
             }
            return values
         } else {
             val values = mutableMapOf<Any?, Any?>()
             any.fieldNames().forEach {
                 values[it] = any.get(it)
             }
             return values
        }
    }
}

Then, on your HOCON Mapper

val module = SimpleModule()

module.setDeserializerModifier(object: BeanDeserializerModifier() {
      override fun modifyMapDeserializer(config: DeserializationConfig, type: MapType, beanDesc: BeanDescription, deserializer: JsonDeserializer<*>): JsonDeserializer<*> {
          return FixedMapDeserializer()
      }
})

mapper.registerModule(module)

The deserializer is a very simple & quick fix: If the JSON input is a array but Jackson expects a class, it converts the JSON to a Map (first array entry = 0 to mapValue on the map), if else, it just parses as a map.

I didn't test it on every possible combination, but on my quick tests: It works pretty well!

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

1 participant