JacksonHelper.kt

package com.hexagonkt.serialization.jackson

import com.fasterxml.jackson.core.JsonFactory
import com.fasterxml.jackson.core.JsonParser.Feature.ALLOW_SINGLE_QUOTES
import com.fasterxml.jackson.core.JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES
import com.fasterxml.jackson.core.json.JsonReadFeature.*
import com.fasterxml.jackson.databind.*
import com.fasterxml.jackson.databind.DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY
import com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_MISSING_CREATOR_PROPERTIES
import com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES
import com.fasterxml.jackson.databind.MapperFeature.SORT_PROPERTIES_ALPHABETICALLY
import com.fasterxml.jackson.databind.SerializationFeature.FAIL_ON_EMPTY_BEANS
import com.fasterxml.jackson.databind.json.JsonMapper
import com.fasterxml.jackson.databind.node.*

object JacksonHelper {

    fun createObjectMapper(mapperFactory: JsonFactory): JsonMapper =
        JsonMapper
            .builder(mapperFactory)
            .configure(FAIL_ON_UNKNOWN_PROPERTIES, false)
            .configure(FAIL_ON_EMPTY_BEANS, false)
            .configure(ALLOW_UNQUOTED_FIELD_NAMES, true)
            .configure(ALLOW_JAVA_COMMENTS, true)
            .configure(ALLOW_SINGLE_QUOTES, true)
            .configure(ALLOW_TRAILING_COMMA, true)
            .configure(ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER, true)
            .configure(ALLOW_LEADING_DECIMAL_POINT_FOR_NUMBERS, true)
            .configure(ALLOW_UNESCAPED_CONTROL_CHARS, true)
            .configure(FAIL_ON_MISSING_CREATOR_PROPERTIES, false)
            .configure(ACCEPT_SINGLE_VALUE_AS_ARRAY, true)
            .configure(SORT_PROPERTIES_ALPHABETICALLY, false)
            .build()

    fun nodeToCollection(node: JsonNode): Any? =
        when (node) {

            is ArrayNode -> node.toList().map { nodeToCollection(it) }
            is ObjectNode -> {
                var map = emptyMap<String, Any?>()

                for (f in node.fields())
                    map = map + (f.key to nodeToCollection(f.value))

                map
            }

            is TextNode -> node.textValue()
            is BigIntegerNode -> node.bigIntegerValue()
            is BooleanNode -> node.booleanValue()
            is DecimalNode -> node.doubleValue()
            is DoubleNode -> node.doubleValue()
            is FloatNode -> node.floatValue()
            is IntNode -> node.intValue()
            is LongNode -> node.longValue()
            is NullNode -> null
            is BinaryNode -> node.binaryValue()

            else -> error("Unknown node type: ${node::class.qualifiedName}")
        }

    fun mapNode(node: JsonNode): Any =
        nodeToCollection(node) ?: error("Parsed content is 'null'")
}