#json

JSON是一种独立于编程语言的数据交换格式,采用文本格式来表述数据,对于数据库系统,支持JSON格式可以使得应用和数据库之间传递数据变得更简洁、易懂和有结构逻辑。

YashanDB支持使用标准的JSON格式来定义和计算json数据,包括其两种结构形式和六种数据类型。

关于JSON的标准语法描述详见其官方网站ECMA-404 The JSON Data Interchange Standard (opens new window)

# JSON的结构形式

键值对形式

这种结构的json数据是一组无序的"键(Key):值(Value)"的集合,该集合通过{开始,以右括号}结束,多个"键(Key):值(Value)"之间使用分割。如下所示:

{
    "key1": "abc",
    "key2": 123,
    "key3": true,
    "key4": null
}

数组形式

这种结构的json数据是一组有序的值(Value)的集合,该集合通过[开始,以]结束,Value之间使用逗号,分割。如下所示:

[
	"ghi", 
	789
]

多结构嵌套混合

一个json数据可以由多个结构形式嵌套混合组成,以满足各种业务数据组织要求。如下所示:

{
    "key1": "abc",
    "key2": 123,
    "key3": true,
    "key4": null,
    "key5": [
        "def", 
        456, 
        false, 
        null, 
       	[
            "ghi", 
            789
        ], 
        {
            "key": "value"
        }
    ],
	"key6": {
        "key": "value"
    }
}

# JSON的数据类型

根据标准JSON格式定义,json数据里的Value可以为如下类型:

  • 对象(Object)
  • 数组(Array)
  • 字符串(String)
  • 数字(Number)
  • 布尔值(True/False)
  • 空值(Null)

其中:

1.对象(Object)和数组(Array)中的值(Value)可以为字符串(String)、数字(Number)、布尔值(True/False)、空值(Null)或者嵌套的对象(Object)和数组(Array)。

2.字符串(String)是由零个或者更多个Unicode字符组成的序列,前后使用双引号"来标识,且支持使用反斜杠\进行转义。

3.布尔值和空值必须采用全小写输入,即true/false/null。

# 路径表达式

YashanDB提供了一些内置函数实现对一个json数据内部的搜索查找功能(例如检索某个Key是否存在于某个json数据中),这需要结合路径表达式(Path Expression)一起使用。

路径表达式有点类似于XML数据的XQuery或XPath表达式,其语法定义如下:

json_path::=

syntax
$ nonfunction_steps function_step

nonfunction_steps::=

syntax
object_step array_step descendent_step

object_step::=

syntax
. * key

array_step::=

syntax
[ * array_index array_n to array_m , ]

descendent_step::=

syntax
.. key

function_step::=

syntax
. item_method ()

item_method::=

syntax
count size type

路径表达式使用绝对路径,以符号$开始,其含义用于表述检索json数据时的匹配路径。$后面跟着零个或者多个nonfunction_steps,以及一个可选的function_step,即支持多步骤匹配;不跟任何内容时,表示与json数据完全一致的匹配路径。

一个路径表达式会选择零个或者多个匹配的值。一般情况下,路径表达式会依次尝试匹配路径表达式中的每一个步骤。如果当前步骤匹配失败,则不会尝试匹配后续步骤,并且路径表达式匹配失败。如果每一个步骤都匹配成功,则路径表达式匹配成功。

其中路径表达式中的key在无双引号的情况下只允许字母数字,不允许任何其余字符出现,需要使用特殊字符时需要按标准json string语法在字符串的前后加上双引号。

示例

SELECT JSON_QUERY(JSON('{"1":1,"2":2,"3":3}'), '$') res FROM dual;
RES                                                              
---------------------------------------------------------------- 
{"1":1,"2":2,"3":3} 

# nonfunction_steps

非函数类匹配步骤,可以为object_step、array_step或者descendent_step中的某一个。

# object_step

针对键值对结构的json数据的匹配步骤,以.开始,后面跟着一个Key或者一个通配符*

Key如果不使用双引号标识时,必须以大写或者小写字母a-z开头,且只能包含字母或者十进制数字(0-9),否则必须使用双引号进行标识。

Key可以为空,如果为空,则必须写成""的形式。

使用object_step匹配时,系统将按指定的Key检索json数据,并返回Key对应的Value。指定通配符时,返回所有的Value。

示例

SELECT JSON_QUERY(JSON('{"1":1,"2":2,"3":3}'), '$.*' WITH WRAPPER) res FROM dual;
RES                                                              
---------------------------------------------------------------- 
[1,2,3]   

SELECT JSON_QUERY(JSON('{"1":1,"2":2,"3":3}'), '$.""') res FROM dual;
RES                                                              
---------------------------------------------------------------- 
   
SELECT JSON_QUERY(JSON('{"1":1,"2":2,"3":3}'), '$."1"') res FROM dual;   
RES                                                              
---------------------------------------------------------------- 
1   

# array_step

针对数组结构的json数据的匹配步骤,以[开始,以]结束,不允许出现空的[],[]中间的内容形式可以为:

  • 通配符*:匹配所有Value。
  • 数组索引位置array_index:按Value在数组中的位置匹配,可以同时指定多个位置匹配,以,分隔。数组位置为从0开始的整数,即数组的第一个元素的索引为0。也可以是last - N的形式,表示从数组尾部开始查找,例如last - 1表示索引数组中倒数第二个元素。单独的last等价于last - 0,表示索引数组中最后一个元素。
  • 数组索引范围array_n "to" array_m:其形式为N to M,N和M都是位置索引,按Value在数组中的位置范围进行匹配,等价于显式地指定从N到M的所有索引位置,例如[2 to 4]等价于[2, 3, 4]。其中N和M的大小顺序没有特殊要求,例如,表示第三到第六个元素的索引范围可以是[2 to 5],也可以是[5 to 2]。需要注意的是[N to N]等价于[N]而不是[N, N]

使用array_step匹配时,上述三种形式必须至少出现一个,但是通配符只能独立出现。

当数组索引位置和数组索引范围可以出现多个,当多个索引位置存在重叠时,重叠区域中的元素会被多次匹配到。

如果指定的索引位置或者索引范围超过了数组本身的范围,则系统将认为没有匹配到数据,而不会报错。

示例

SELECT JSON_QUERY(JSON('[1,2,3,4,5,6,7,8,9]'), '$[0 to 2, 5 to 3, last to last - 2, 1, 1]' WITH WRAPPER) res FROM dual;
RES                                                              
---------------------------------------------------------------- 
[1,2,3,4,5,6,7,8,9,2,2]  

# descendent_step

针对键值对结构的json数据的递归匹配步骤,以两个连续的句号..开始,后面跟着一个Key。它基于当前步骤之前匹配到的数据,进行递归查找,返回所有匹配到的值。

示例

SELECT JSON_QUERY(JSON('{ "a" : { "b" : { "d" : 1 },
          						  "c" : [ 2, { "d" : 3 } ],
         						  "d" : 4 },
						  "d" : 5 }'), '$.a..d' WITH WRAPPER) res FROM dual;
RES                                                              
---------------------------------------------------------------- 
[4,1,3] 

# function_step

函数类匹配步骤,function_step在路径表达式中不是必须出现的,如果出现,它只能是路径表达式的最后一步。function_step以.开始,后面跟着函数方法名称,及左右括号(),括号之间可以有空格。

# item_method

函数方法名称,函数方法应用于当前步骤之前匹配到的数据,对其进行数据转换,包括如下几种:

  • count:返回当前步骤之前匹配到的数据的数量。
  • size:如果当前步骤之前匹配到的数据为数组,返回数组的元素数量,如果不是数组,返回NULL。
  • type:返回当前步骤之前匹配到的数据的类型。

示例

SELECT JSON_QUERY(JSON('{ "a" : { "b" : { "d" : 1 },
          						  "c" : [ 2, { "d" : 3 } ],
         						  "d" : 4 },
						  "d" : 5 }'), '$.a..d.count()' WITH WRAPPER) res FROM dual;
RES                                                              
---------------------------------------------------------------- 
[3]

# 宽松匹配

在严格匹配的模式下,根据路径表达式对json数据进行检索时,路径表达式中的某个匹配步骤只能应用于特定的数据类型,具体要求为:

  • object_step只能对Object类型数据进行查找,应用于其他类型均认为匹配失败。
  • array_step只能对Array类型数据进行查找,应用于其他类型均认为匹配失败。

为了提供更灵活的查询,YashanDB支持宽松匹配模式(系统默认为此种模式),具体规则为:

  • 如果当前为array_step匹配,但json数据为非Array类型,则系统自动将json数据封装为长度为1的数组,然后进行匹配。

    示例

    SELECT JSON_QUERY(JSON('1'), '$[0]') res FROM dual;
    RES                                                              
    ---------------------------------------------------------------- 
    1   
    
  • 如果当前为非array_step匹配,如object_step、function_step等,但json数据为Array类型,则系统对Array中的每个元素应用当前的匹配路径。

    示例

    SELECT JSON_QUERY(JSON('[{"a": 1}, {"a": 2}, {"a": 3}]'), '$.a' WITH WRAPPER) res FROM dual;
    RES                                                              
    ---------------------------------------------------------------- 
    [1,2,3]