簡體   English   中英

Groovy:驗證JSON字符串

[英]Groovy: validate JSON string

我需要在Groovy中檢查字符串是否是有效的JSON。 我的第一個想法是通過new JsonSlurper().parseText(myString)發送它,如果沒有異常,則認為它是正確的。

但是,我發現Groovy很樂意接受JsonSlurper尾隨逗號,但JSON 不允許使用尾隨逗號 有沒有一種簡單的方法來驗證Groovy中的JSON是否符合官方JSON規范?

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}]}

正如您所看到的,獲勝者是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依賴。 希望能幫助到你。

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

嘗試另一個json解析器

我正在使用snakeyaml因為它支持json和yaml,但你可以通過互聯網找到其他基於java的json解析器庫

@Grab(group='org.yaml', module='snakeyaml', version='1.19')

def jsonString = '''{"a":1,"b":2}...'''

//no error in the next line
def json1 = new groovy.json.JsonSlurper().parseText( jsonString )
//the following line fails
def json2 = new org.yaml.snakeyaml.Yaml().load( jsonString )

可以像這樣驗證:

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

或者更清潔一點:

String.metaClass.isJson << { ->
    def normalize = { it.replaceAll("\\s", "") }

    try {
        normalize(delegate) == normalize(JsonOutput.toJson(new JsonSlurper().parseText(delegate)))
    } catch (e) {
        false
    }
}

assert '{"key":"value"}'.isJson()
assert !''.isJson()

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM