Groovy:验证JSON字符串

时间:2018-01-26 20:39:32

标签: json validation groovy jsonslurper

我需要在Groovy中检查字符串是否是有效的JSON。我的第一个想法是通过new JsonSlurper().parseText(myString)发送它,如果没有例外,则认为它是正确的。

但是,我发现Groovy很乐意接受带有JsonSlurper的尾随逗号,但是JSON doesn't allow trailing commas。有没有一种简单的方法来验证Groovy中的JSON是否符合官方JSON规范?

3 个答案:

答案 0 :(得分:3)

JsonSlurper类使用JsonParser接口实现(JsonParserCharArray是默认接口)。这些解析器通过char检查char是什么是当前字符以及它代表什么类型的令牌类型。如果您查看第139行的JsonParserCharArray.decodeJsonObject()方法,您会看到如果解析器看到}字符,它会中断循环并完成解码JSON对象并忽略}之后存在的任何内容。

这就是为什么如果在JSON对象前放置任何无法识别的字符,JsonSlurper将抛出异常。但是如果你在}之后用任何不正确的字符结束你的JSON字符串,它将会通过,因为解析器甚至不考虑这些字符。

解决方案

您可以考虑使用JsonOutput.prettyPrint(String json)方法,如果涉及它尝试打印的JSON,则使用更为严格的方法(它使用JsonLexer以流方式读取JSON令牌)。如果你这样做:

def jsonString = '{"name": "John", "data": [{"id": 1},{"id": 2}]}...'

JsonOutput.prettyPrint(jsonString)

它会抛出一个例外:

Exception in thread "main" groovy.json.JsonException: Lexing failed on line: 1, column: 48, while reading '.', no possible valid JSON value or punctuation could be recognized.
    at groovy.json.JsonLexer.nextToken(JsonLexer.java:83)
    at groovy.json.JsonLexer.hasNext(JsonLexer.java:233)
    at groovy.json.JsonOutput.prettyPrint(JsonOutput.java:501)
    at groovy.json.JsonOutput$prettyPrint.call(Unknown Source)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:113)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:125)
    at app.JsonTest.main(JsonTest.groovy:13)

但是如果我们传递一个有效的JSON文档,如:

def jsonString = '{"name": "John", "data": [{"id": 1},{"id": 2}]}'

JsonOutput.prettyPrint(jsonString)

它会顺利通过。

好消息是,您不需要任何额外的依赖来验证您的JSON。

更新:针对多种不同情况的解决方案

我做了一些调查并使用3种不同的解决方案进行测试:

  • JsonOutput.prettyJson(String json)
  • JsonSlurper.parseText(String json)
  • ObjectMapper.readValue(String json, Class<> type)(需要添加jackson-databind:2.9.3依赖项)

我使用了以下JSON作为输入:

def json1 = '{"name": "John", "data": [{"id": 1},{"id": 2},]}'
def json2 = '{"name": "John", "data": [{"id": 1},{"id": 2}],}'
def json3 = '{"name": "John", "data": [{"id": 1},{"id": 2}]},'
def json4 = '{"name": "John", "data": [{"id": 1},{"id": 2}]}... abc'
def json5 = '{"name": "John", "data": [{"id": 1},{"id": 2}]}'

预期结果是前4个JSON验证失败,只有第5个JSON正确。为了测试它,我创建了这个Groovy脚本:

@Grab(group='com.fasterxml.jackson.core', module='jackson-databind', version='2.9.3')

import groovy.json.JsonOutput
import groovy.json.JsonSlurper
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.DeserializationFeature

def json1 = '{"name": "John", "data": [{"id": 1},{"id": 2},]}'
def json2 = '{"name": "John", "data": [{"id": 1},{"id": 2}],}'
def json3 = '{"name": "John", "data": [{"id": 1},{"id": 2}]},'
def json4 = '{"name": "John", "data": [{"id": 1},{"id": 2}]}... abc'
def json5 = '{"name": "John", "data": [{"id": 1},{"id": 2}]}'

def test1 = { String json ->
    try {
        JsonOutput.prettyPrint(json)
        return "VALID"
    } catch (ignored) {
        return "INVALID"
    }
}

def test2 = { String json ->
    try {
        new JsonSlurper().parseText(json)
        return "VALID"
    } catch (ignored) {
        return "INVALID"
    }
}

ObjectMapper mapper = new ObjectMapper()
mapper.configure(DeserializationFeature.FAIL_ON_TRAILING_TOKENS, true)

def test3 = { String json ->
    try {
        mapper.readValue(json, Map)
        return "VALID"
    } catch (ignored) {
        return "INVALID"
    }
}

def jsons = [json1, json2, json3, json4, json5]
def tests = ['JsonOutput': test1, 'JsonSlurper': test2, 'ObjectMapper': test3]

def result = tests.collectEntries { name, test ->
    [(name): jsons.collect { json ->
        [json: json, status: test(json)]
    }]
}

result.each {
    println "${it.key}:"
    it.value.each {
        println " ${it.status}: ${it.json}"
    }
    println ""
}

结果如下:

JsonOutput:
 VALID: {"name": "John", "data": [{"id": 1},{"id": 2},]}
 VALID: {"name": "John", "data": [{"id": 1},{"id": 2}],}
 VALID: {"name": "John", "data": [{"id": 1},{"id": 2}]},
 INVALID: {"name": "John", "data": [{"id": 1},{"id": 2}]}... abc
 VALID: {"name": "John", "data": [{"id": 1},{"id": 2}]}

JsonSlurper:
 INVALID: {"name": "John", "data": [{"id": 1},{"id": 2},]}
 VALID: {"name": "John", "data": [{"id": 1},{"id": 2}],}
 VALID: {"name": "John", "data": [{"id": 1},{"id": 2}]},
 VALID: {"name": "John", "data": [{"id": 1},{"id": 2}]}... abc
 VALID: {"name": "John", "data": [{"id": 1},{"id": 2}]}

ObjectMapper:
 INVALID: {"name": "John", "data": [{"id": 1},{"id": 2},]}
 INVALID: {"name": "John", "data": [{"id": 1},{"id": 2}],}
 INVALID: {"name": "John", "data": [{"id": 1},{"id": 2}]},
 INVALID: {"name": "John", "data": [{"id": 1},{"id": 2}]}... abc
 VALID: {"name": "John", "data": [{"id": 1},{"id": 2}]}

正如您所看到的,获胜者是杰克逊的ObjectMapper.readValue()方法。重要的是 - 它与jackson-databind&gt; = 2.9.0一起使用。在这个版本中,他们引入了DeserializationFeature.FAIL_ON_TRAILING_TOKENS,这使得JSON解析器按预期工作。如果我们不像上面的脚本那样将此配置功能设置为true,则ObjectMapper会产生错误的结果:

ObjectMapper:
 INVALID: {"name": "John", "data": [{"id": 1},{"id": 2},]}
 INVALID: {"name": "John", "data": [{"id": 1},{"id": 2}],}
 VALID: {"name": "John", "data": [{"id": 1},{"id": 2}]},
 VALID: {"name": "John", "data": [{"id": 1},{"id": 2}]}... abc
 VALID: {"name": "John", "data": [{"id": 1},{"id": 2}]}

我很惊讶Groovy的标准库在此测试中失败了。幸运的是,它可以通过jackson-databind:2.9.x依赖来完成。希望它有所帮助。

答案 1 :(得分:2)

似乎是groovy json解析器中的错误或功能

尝试另一个json解析器

我正在使用snakeyaml,因为它支持json和yaml,但你可以通过互联网找到其他基于java的json解析器库

<ScrollView onClick={()=>{
    if(this.state.showMoreModal){
        this.setState({showMoreModal:false});
    }
}}>
...
<Confirm/>
</ScrollView>

答案 2 :(得分:0)

可以这样验证:

Microsoft.VisualBasic.Activities

或更干净一点:

assert JsonOutput.toJson(new JsonSlurper().parseText(myString)).replaceAll("\\s", "") ==
            myString.replaceAll("\\s", "")