json - Groovy:验证 JSON 字符串

标签 json validation groovy jsonslurper

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

但是,我发现 Groovy 很乐意接受 JsonSlurper 的尾随逗号,但 JSON doesn't allow trailing commas 。有没有一种简单的方法可以在 Groovy 中验证 JSON 并遵守官方 JSON 规范?

最佳答案

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

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

解决方案

您可以考虑使用JsonOutput.prettyPrint(String json)如果涉及到尝试打印的 JSON,则该方法会受到更多限制(它使用 JsonLexer 以流式传输方式读取 JSON token )。如果您这样做:

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 个正确。为了测试它,我创建了这个 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}]}

如您所见,获胜者是 Jackson 的 ObjectMapper.readValue()方法。重要的是 - 它适用于 jackson-databind >= 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来完成。依赖性。希望对您有所帮助。

关于json - Groovy:验证 JSON 字符串,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48469200/

相关文章:

java - 了解 Gradle/Groovy 任务的微妙之处

javascript - Jquery帮助加载Json数据

javascript - 正确的 JSON 格式

groovy - 如何使用groovy更快地连接到jmx

php - 验证表单后禁用 Bootstrap 验证器

asp.net - Web 用户控制和验证

html - Groovy/Grails Hibernate 无法创建 bean 事务

java - JSONObject 文本必须以 '{' 开头

json - 将列表转换到dart中的对象列表中

java - 使用 servlet 在服务器端验证单选按钮