0

I do work for a project currently, where the data is send to the server as application/x-www-form-urlencoded (which is bad, and it should be JSON, but unfortunately I am not able to change this one).

My question is, how can I transfer the given structure to JSON, or, even better, deserialize it directly to an object?

_id=[5bfad95450642c333010daca], 
_rev=[1-9ce33949c3acd85cea6c58467e6a8144], 
type=[Group], 
user=[aUSer], 
default=[aDetail], 
store[aDetail][prop]=[5], 
store[aDetail][lprop1][0][time]=[00:00], 
store[aDetail][lprop1][0][value]=[14], 
store[aDetail][lprop1][0][timeAsSeconds]=[0],
store[aDetail][lprop1][1][time]=[07:00], 
store[aDetail][lprop1][1][value]=[8], 
store[aDetail][lprop1][1][timeAsSeconds]=[25200], 
store[aDetail][anprop]=[25], 
store[aDetail][lprop2][0][time]=[00:00], 
store[aDetail][lprop2][0][value]=[61], 
store[aDetail][lprop2][0][timeAsSeconds]=[0],   
store[bDetail][prop]=[6], 
store[bDetail][lprop1][0][time]=[00:10], 
store[bDetail][lprop1][0][value]=[12], 
store[bDetail][lprop1][0][timeAsSeconds]=[0],
store[bDetail][lprop1][1][time]=[07:10], 
store[bDetail][lprop1][1][value]=[9], 
store[bDetail][lprop1][1][timeAsSeconds]=[25200], 
store[bDetail][anprop]=[25], 
store[bDetail][lprop2][0][time]=[00:00], 
store[bDetail][lprop2][0][value]=[61], 
store[bDetail][lprop2][0][timeAsSeconds]=[0], 
created_at=[2018-01-11T20:48:22.574+0100]

In json, this would look something like this (skippen most of the already given values):

{
_id: 5bfad95450642c333010daca,
_rev: 1-9ce33949c3acd85cea6c58467e6a8144,
type: Group,
user: aUSer,
default: aDetail,
store: [
  aDetail: {
    prop: 0,
    lprop1: [
      {
        time: 00:00,
        value: 14,
        timeAsSeconds: 0
      }
    ]
  }
]
}   

The conversion from this form format to json is rather annoying. Any help is appreciated. BTW, me is using Jackson and Java and/or Kotlin, if this is of any help.

triplem
  • 1,324
  • 13
  • 26

1 Answers1

0

I have a rather naive solution, which takes nested objects and arrays into account. This works for me, and there can/will be some major drawbacks, if used in other environments.

The solution is written in kotlin but most probably could be written in a not as complex way.

fun parseToMap(map: MutableMap<String, Any>, key: String, value: Any): MutableMap<String, Any> {
    val cleanedV = if (value is String) URLDecoder.decode(value, "UTF-8") else value

    if (!key.contains("[")) {
        map.putIfAbsent(key, cleanedV)
    } else {
        // mapKey is the key going to get stored in the map
        val mapKey = key.substring(0, key.indexOf("["))

        // nextKey is the next key pushed to the next call of parseToMap
        var nextKey = key.removePrefix(mapKey)
        nextKey = nextKey.replaceFirst("[", "").replaceFirst("]", "")

        var isArray = false
        var index = -1

        if (nextKey.contains("[") &&
                nextKey.substring(0, nextKey.indexOf("[")).matches(Regex("[0-9]+"))) {
            index = nextKey.substring(0, nextKey.indexOf("[")).toInt()
            isArray = true
        }

        // mapkey used for object in list
        val newMapKey = if (isArray) nextKey.substring(nextKey.indexOf("[") + 1, nextKey.indexOf("]")) else ""

        val child: Any?
        var childMap: MutableMap<String, Any> = mutableMapOf()

        if (map.containsKey(mapKey)) {
            println("key $mapKey exists already")
            child = map[mapKey]

            when (child) {
                is MutableList<*> -> {
                    if (child == null || child.isEmpty()) {
                        childMap = mutableMapOf()
                        val tmpList = child as MutableList<Any>
                        tmpList.add(childMap)
                        map.put(newMapKey, tmpList)
                    } else {
                        if (child.size > index) {
                            childMap = child.get(index) as MutableMap<String, Any>
                            childMap = parseToMap(childMap, newMapKey, value)
                        } else {
                            childMap = parseToMap(childMap, newMapKey, value)
                            val tmpList = child as MutableList<Any>
                            tmpList.add(childMap)
                        }
                    }
                }
                is MutableMap<*, *> -> childMap = map.get(mapKey) as MutableMap<String, Any>
            }
        } else {
            if (isArray) {
                child = mutableListOf<Any>()
                childMap = parseToMap(childMap, newMapKey, value)
                child.add(childMap)
                map.put(mapKey, child)
            } else {
                childMap = mutableMapOf<String, Any>()
            }
        }

        if (!isArray) parseToMap(childMap, nextKey, value)

        map.putIfAbsent(mapKey, childMap)
    }

    return map
}

This method should be called with a Map and each parameter key (like bespoken in my question already) as well as the value of each parameter.

triplem
  • 1,324
  • 13
  • 26