解析和生成 JSON
Groovy 集成支持在 Groovy 对象和 JSON 之间进行转换。专门用于 JSON 序列化和解析的类位于 groovy.json
包中。
1. JsonSlurper
JsonSlurper
是一个类,它将 JSON 文本或读取器内容解析为 Groovy 数据结构(对象),例如映射、列表以及 Integer
、Double
、Boolean
和 String
等原始类型。
该类附带了大量重载的 parse
方法以及一些特殊方法,例如 parseText
、parseFile
等。对于下一个示例,我们将使用 parseText
方法。它解析 JSON String
并递归将其转换为对象列表或映射。其他 parse*
方法类似,它们返回 JSON String
,但适用于不同的参数类型。
def jsonSlurper = new JsonSlurper()
def object = jsonSlurper.parseText('{ "name": "John Doe" } /* some comment */')
assert object instanceof Map
assert object.name == 'John Doe'
请注意,结果是一个普通的映射,可以像普通的 Groovy 对象实例一样处理。JsonSlurper
根据 ECMA-404 JSON 交换标准解析给定的 JSON,并支持 JavaScript 注释和日期。
除了映射之外,JsonSlurper
还支持转换为列表的 JSON 数组。
def jsonSlurper = new JsonSlurper()
def object = jsonSlurper.parseText('{ "myList": [4, 8, 15, 16, 23, 42] }')
assert object instanceof Map
assert object.myList instanceof List
assert object.myList == [4, 8, 15, 16, 23, 42]
JSON 标准支持以下原始数据类型:string、number、object、true
、false
和 null
。JsonSlurper
将这些 JSON 类型转换为相应的 Groovy 类型。
def jsonSlurper = new JsonSlurper()
def object = jsonSlurper.parseText '''
{ "simple": 123,
"fraction": 123.66,
"exponential": 123e12
}'''
assert object instanceof Map
assert object.simple.class == Integer
assert object.fraction.class == BigDecimal
assert object.exponential.class == BigDecimal
由于 JsonSlurper
返回纯 Groovy 对象实例,后端没有任何特殊的 JSON 类,因此其使用是透明的。实际上,JsonSlurper
的结果符合 GPath 表达式。GPath 是一种强大的表达式语言,受多种不同数据格式的 slurper 支持(XmlSlurper
用于 XML 就是一个示例)。
有关更多详细信息,请参阅有关 GPath 表达式的部分。 |
下表概述了 JSON 类型和相应的 Groovy 数据类型
JSON | Groovy |
---|---|
string |
|
number |
|
object |
|
array |
|
true |
|
false |
|
null |
|
date |
基于 |
当 JSON 中的值为 null 时,JsonSlurper 会将其补充为 Groovy 的 null 值。这与其他 JSON 解析器不同,后者使用库提供的单例对象表示 null 值。 |
1.1. 解析器变体
JsonSlurper
附带了几个解析器实现。每个解析器都适合不同的需求,对于某些场景,JsonSlurper
默认解析器可能不是所有情况下的最佳选择。以下是随附解析器实现的概述
-
JsonParserCharArray
解析器基本上接受一个 JSON 字符串,并对其底层字符数组进行操作。在值转换期间,它会复制字符子数组(一种称为“切片”的机制)并对其进行操作。 -
JsonFastParser
是JsonParserCharArray
的一个特殊变体,是最快的解析器。然而,它并非默认解析器是有原因的。JsonFastParser
是一种所谓的索引覆盖解析器。在解析给定的 JSONString
期间,它会尽可能避免创建新的字符数组或String
实例。它只保留指向底层原始字符数组的指针。此外,它会尽可能晚地延迟对象创建。如果将解析的映射放入长期缓存中,则必须小心,因为映射对象可能尚未创建,并且仍然仅由指向原始字符缓冲区的指针组成。然而,JsonFastParser
带有一种特殊的切片模式,它会尽早切割字符缓冲区,以保留原始缓冲区的一个小副本。建议将JsonFastParser
用于 2MB 以下的 JSON 缓冲区,并牢记长期缓存限制。 -
JsonParserLax
是JsonParserCharArray
解析器的一个特殊变体。它具有与JsonFastParser
相似的性能特征,但不同之处在于它并非完全依赖于 ECMA-404 JSON 语法。例如,它允许注释、无引号字符串等。 -
JsonParserUsingCharacterSource
是一个用于非常大的文件的特殊解析器。它使用一种称为“字符窗口”的技术,以恒定的性能特性解析大型 JSON 文件(在这种情况下,大型意味着文件大小超过 2MB)。
JsonSlurper
的默认解析器实现是 JsonParserCharArray
。JsonParserType
枚举包含上述解析器实现的常量
实现 | 常量 |
---|---|
|
|
|
|
|
|
|
|
更改解析器实现就像通过调用 JsonSlurper#setType()
设置 JsonParserType
一样简单。
def jsonSlurper = new JsonSlurper(type: JsonParserType.INDEX_OVERLAY)
def object = jsonSlurper.parseText('{ "myList": [4, 8, 15, 16, 23, 42] }')
assert object instanceof Map
assert object.myList instanceof List
assert object.myList == [4, 8, 15, 16, 23, 42]
2. JsonOutput
JsonOutput
负责将 Groovy 对象序列化为 JSON 字符串。它可以被视为 JsonSlurper 的配套对象,它是一个 JSON 解析器。
JsonOutput
带有重载的静态 toJson
方法。每个 toJson
实现都接受不同的参数类型。静态方法可以直接使用,也可以通过静态导入语句导入。
toJson
调用的结果是一个包含 JSON 代码的 String
。
def json = JsonOutput.toJson([name: 'John Doe', age: 42])
assert json == '{"name":"John Doe","age":42}'
JsonOutput
不仅支持将原始类型、映射或列表数据类型序列化为 JSON,它甚至进一步支持序列化 POGO,即普通的 Groovy 对象。
class Person { String name }
def json = JsonOutput.toJson([ new Person(name: 'John'), new Person(name: 'Max') ])
assert json == '[{"name":"John"},{"name":"Max"}]'
2.1. 自定义输出
如果您需要控制序列化的输出,可以使用 JsonGenerator
。JsonGenerator.Options
构建器可用于创建自定义生成器。可以在此构建器上设置一个或多个选项以更改生成的输出。设置完选项后,只需调用 build()
方法即可获取一个完全配置的实例,该实例将根据所选选项生成输出。
class Person {
String name
String title
int age
String password
Date dob
URL favoriteUrl
}
Person person = new Person(name: 'John', title: null, age: 21, password: 'secret',
dob: Date.parse('yyyy-MM-dd', '1984-12-15'),
favoriteUrl: new URL('https://groovy-lang.cn/'))
def generator = new JsonGenerator.Options()
.excludeNulls()
.dateFormat('yyyy@MM')
.excludeFieldsByName('age', 'password')
.excludeFieldsByType(URL)
.build()
assert generator.toJson(person) == '{"name":"John","dob":"1984@12"}'
闭包可用于转换类型。这些闭包转换器针对给定类型注册,并且在遇到该类型或子类型时将随时调用。闭包的第一个参数是与注册转换器的类型匹配的对象,此参数是必需的。闭包可以采用可选的第二个 String
参数,如果可用,此参数将设置为键名。
class Person {
String name
URL favoriteUrl
}
Person person = new Person(name: 'John', favoriteUrl: new URL('https://groovy-lang.cn/json.html#_jsonoutput'))
def generator = new JsonGenerator.Options()
.addConverter(URL) { URL u, String key ->
if (key == 'favoriteUrl') {
u.getHost()
} else {
u
}
}
.build()
assert generator.toJson(person) == '{"name":"John","favoriteUrl":"groovy-lang.org"}'
// No key available when generating a JSON Array
def list = [new URL('https://groovy-lang.cn/json.html#_jsonoutput')]
assert generator.toJson(list) == '["https://groovy-lang.cn/json.html#_jsonoutput"]'
// First parameter to the converter must match the type for which it is registered
shouldFail(IllegalArgumentException) {
new JsonGenerator.Options()
.addConverter(Date) { Calendar cal -> }
}
2.1.1. 格式化输出
正如我们在前面的示例中看到的,JSON 输出默认情况下未进行美化打印。然而,JsonOutput
中的 prettyPrint
方法可以解决此任务。
def json = JsonOutput.toJson([name: 'John Doe', age: 42])
assert json == '{"name":"John Doe","age":42}'
assert JsonOutput.prettyPrint(json) == '''\
{
"name": "John Doe",
"age": 42
}'''.stripIndent()
prettyPrint
接受一个 String
作为单个参数;因此,它可以应用于任意 JSON String
实例,而不仅仅是 JsonOutput.toJson
的结果。
2.2. 构建器
从 Groovy 创建 JSON 的另一种方法是使用 JsonBuilder
或 StreamingJsonBuilder
。这两个构建器都提供了一个 DSL,允许构建对象图,然后将其转换为 JSON。
有关构建器的更多详细信息,请参阅构建器章节,其中涵盖了 JsonBuilder 和 StreamingJsonBuilder。 |