推断 Groovy 中的字符串值类型

Inferring String value type in Groovy

我有一种情况,我将得到一个字符串,我需要确定什么 Class<?> 最适合它的值,给定以下限制:

我最好的尝试是令人讨厌的并且涉及很多嵌套的 try/catch 块:

// Groovy pseudo-code
Class<?> determineValueType(String value) {
    Class<?> clazz
    if(value.equalsIgnoreCase('true') || value.equalsIgnoreCase('false')) {
        clazz = Boolean
    } else {
        try {
            Integer.parse(value)
            clazz = Integer
        } catch(Exception ex1) {
            try {
                Double.parse(value)
                clazz = Double
            } catch(Exception ex2) {
                try {
                    Date.parse('YYYY-MM-DD hh:mm:ss.sss', value)
                    clazz = Date
                } catch(Exception ex3) {
                    clazz = String
                }
            }
        }
    }

    clazz
}

是否有任何 Groovier 方法来实现这一点,也许是某些晦涩 Groovy 反射 API 所特有的东西?

在 Groovy 的扩展 String class(实际上在 CharSequence 中)有两种方法可以帮助您:

但对于其他情况,据我所知,您需要自己实施解析。您可以尝试使用地图和一些闭包来减少一些样板文件:

Class parse(val) {
    def convert = [
        (Integer) : { it.toInteger() },
        (Double)  : { it.toDouble() },
        (Date)    : { Date.parse('YYYY-MM-DD hh:mm:ss.sss', it) },
        (Boolean) : { Boolean.parseBoolean it },
        (String)  : { it }
    ]

    convert.findResult { key, value ->
        try {
            if (value(val)) return key
        } catch (e) {}
    }
}

assert parse('9.1') == Double
assert parse('9') == Integer
assert parse('1985-10-26 01:22:00.000') == Date // great scott!
assert parse('chicken') == String
assert parse('True') == Boolean

请注意,如果 (Double)(Integer) 之前,则测试将不起作用,因为 9 既是双精度又是整数。

Groovy 有一些功能可以让您使这个逻辑更时髦。

  1. 强大的switch语句,支持正则表达式和闭包。
  2. isIntegerisDouble 来自 Groovy JDK 的内置 CharSequence 方法。遗憾的是,没有严格的 isBoolean 所以我们需要自己实施。
  3. safe navigation operator 避免 NPE。

结合这些功能...

Class<?> determineValueType(String value) {
    switch (value) {
        case { ['true', 'false'].contains(value?.toLowerCase()) }: 
            return Boolean
        case { value?.isInteger() }: 
            return Integer
        case { value?.isDouble() }: 
            return Double
        case ~/^\d{4}-\d{2}-\d{2} \d{1,2}:\d{2}:\d{2}\.\d{3}$/: 
            return Date
        default: 
            return String
    }
}

assert determineValueType('true') == Boolean
assert determineValueType('false') == Boolean
assert determineValueType('2039230') == Integer
assert determineValueType('203923.0') == Double
assert determineValueType('2016-07-26 12:00:00.000') == Date
assert determineValueType('foo') == String

我使用正则表达式而不是 SimpleDateFormat 来避免捕获异常。它的语义可能略有不同,但您也可以创建一个辅助方法,如果 Date.parse.

抛出异常,则 returns false