javajsongroovyjsonslurper

How in my example in groovy (java) to automatically detect the json field type?


In my task, I process the incoming json and use the ResultSetReader to return the data as a dataset. All types must be written to "types". They are now defined like this: def types = list.find (). Values ​​() *. GetClass () *. SimpleName

But there are two problems here:

  1. If in the first block of json some field has "null", and in the next block there is a number, then the type is written as "null", and not as "Integer".

  2. If in all json blocks someone's field is "null", then "null" is written, and you need to write, for example, "String" by default, so that the program does not stop working.

    How should I do it? You are kindly requested NOT to rewrite all my code, but to give advice specifically on this issue. "Types" should contain only types of format ["String", "Integer", "String"] (for example).

    There is NO need to advise where in types will be [NameJSON: String, NameJSON: Integer, NameJSON: Sting], because in this case I cannot use ResultSetReader.

import groovy.json.JsonSlurper
import ru.itrpro.xm.plugins.groovy.ResultSetReader;

class XM_PARSE_XLS {

    def execute(ResultSetReader reader, String pfile) {

        def jsonSlurper = new JsonSlurper()
        def list = jsonSlurper.parseText pfile

        List names = list.inject( new LinkedHashSet<>() ){ res, map ->
            res.addAll map.keySet()
            res
        }.toList()
        def types = list.find().values()*.getClass()*.simpleName

        //formation of the dataset header
        reader.outputLinesSetHeaders(names,types);

        list.each{ e ->
            reader.outputLines names.collect{ e[ it ] }
            //println names.collect{ e[ it ] }
        }

        //closing dataset
        reader.outputLinesEnd();
        return null;

    }
    static void main(String... args) {
        String pfile =  """
[{"AUTO":"bmw",
  "HOME":null,
  "JOB":""},
  
  {"AUTO":"audi",
  "HOME":135,
  "JOB":null},
  
  {"AUTO":"opel1",
  "HOME":10,
  "JOB":null}]
"""
        def SSC = new XM_PARSE_XLS()
        def res = SSC.execute(new ResultSetReader(), pfile)
    }
}

Solution

  • Working example here.

    Consider replacing this:

    def types = list.find().values()*.getClass()*.simpleName
    

    with this (edit: updated for question in comments about BigDecimal/double):

    // as requested in comments:
    def getTypeDef(def value) {
        (value instanceof BigDecimal) ? "double" : value.getClass().simpleName
    }
    
    def typeMap = [:].withDefault { key -> "String" }
    list.each { map ->
        map.each { key, value ->
            if (value != null) {
                typeMap[key] = getTypeDef(value)
            }   
        }   
    }   
    def types = names.collect { name -> typeMap[name] }
    

    The idea is that a typeMap maps the field-name to its type, and defaults to String. Then:

    If any item has a proper value for a field, then that value will be used (assuming that items cannot have value of different types). If no item has a value for the field, then typeMap will default to String.