[链接] [链接] MySQL 从 5.7.8 开始,支持原生的 JSON 数据类型。可以高效的访问 JSON 文档中的数据。与在字符串列中存储 JSON 格式字符串相比,JSON 数据类型有以下优势: 可自动验证存储的 JSON 数据格式是否正确。 优化的存储格式。存储在 JSON 列中的 JSON 文档将转化为内部 ..

MySQL 中 JSON 类型应用

MySQL 8.0

The JSON Data Type

MySQL 从 5.7.8 开始,支持原生的 JSON 数据类型。可以高效的访问 JSON 文档中的数据。与在字符串列中存储 JSON 格式字符串相比,JSON 数据类型有以下优势:

JSON 列占用的空间与 LONGBLOB 或 LONGTEXT 基本相同。

在 MySQL 8.0.13 之前,JSON 列不能定义非 null 的默认值。

以下测试 MySQL 版本:8.0.16, 个别测试会与较老的版本结果不一致。

创建JSON值

JSON 数组包含由逗号分隔并由中括号包裹的值列表

["abc", 10, null, true, false]

JSON 对象包含一组由逗号分隔并且由大括号包裹的键值对

{"k1": "value", "k2": 10}

JSON 对象中的键必须是字符串。JSON 数组中的元素和 JSON 对象键值允许嵌套。

[99, {"id": "HK500", "cost": 75.99}, ["hot", "cold"]]
{"k1": "value", "k2": [10, 20]}

JSON 值在插入前会校验正确性。

create table t1 (jdoc JSON);

insert into t1 values
('{"key1":"value1","key2":"value2"}');

insert into t1 values
('"stringvalue"');

insert into t1 values
('[1, 2,');
/**插入的value值中下标6的位置有错
> 3140 - Invalid JSON text: "Invalid value." at position 6 in value for column 't1.jdoc'.*/

JSON_TYPE()函数需要 JSON 参数,并尝试将其解析为 JSON 值。如果值有效,则返回值的 JSON 类型,否则报错。

mysql> SELECT JSON_TYPE('["a", "b", 1]');
> ARRAY 

mysql> SELECT JSON_TYPE('"hello"');
> STRING
mysql> select json_type('{"key":"value"}') type;
> OBJECT

mysql> SELECT JSON_TYPE('hello');
> ERROR 3146 (22032): Invalid data type for JSON data in argument 1
to function json_type; a JSON string or JSON type is required.

JSON_ARRAY()函数可以将传入的参数(允许 null)转化为一个 JSON 数组

mysql> SELECT JSON_ARRAY('a', 1, NOW());
> ["a", 1, "2019-07-13 15:56:47.000000"]

JSON_OBJECT()函数可将传入参数(允许 null)转化为一个 JSON 对象

mysql> SELECT JSON_OBJECT('key1', 1, 'key2', 'abc');
> {"key1": 1, "key2": "abc"}

/**
* JSON对象中key值不能为null,value可以为null*/
mysql> SELECT JSON_OBJECT('A','a',null,null,'C','c');
> JSON documents may not contain NULL member names.

JSON_MERGE_PRESERVE()接受两个或多个 JSON 文档并返回组合结果(关于 JSON 值的合并,下面会有更加详细的测试)

mysql> SELECT JSON_MERGE_PRESERVE('["a", 1]', '{"key": "value"}');
>	["a", 1, {"key": "value"}]

JSON 值可以赋给用户自定义变量

mysql> set @j = JSON_OBJECT('key','value');
mysql> select @j;
> {"key": "value"}

然而,用户定义的变量不能是 JSON 数据类型,因此尽管前面示例中的@j 看起来像 JSON 值并且具有与 JSON 值相同的字符集和排序规则,但它没有 JSON 数据类型。相反,JSON_OBJECT() 的结果在分配给变量时会转换成字符串。

ps:官方文档上的这段话可能只是提示一下用户变量的数据类型不能是 JSON,但是使用起来好像也没什么问题。

下面的例子可以看到,变量是正确的 JSON 格式数据时,使用 JSON 函数对其操作都没问题。

mysql> SET @x = '{ "a": 1, "b": 2 }',
     >     @y = '{ "a": 3, "c": 4 }',
     >     @z = '{ "a": 5, "d": 6 }';

mysql> SELECT  JSON_MERGE_PATCH(@x, @y, @z)    AS Patch,
    ->         JSON_MERGE_PRESERVE(@x, @y, @z) AS Preserve\G

>   Patch: {"a": 5, "b": 2, "c": 4, "d": 6}
>Preserve: {"a": [1, 3, 5], "b": 2, "c": 4, "d": 6}


mysql> SET @j = '["a", ["b", "c"], "d"]';
mysql> SELECT JSON_REMOVE(@j, '$[1]');
> ["a", "d"]

mysql> set @a = Json_object('key','value'), @b = json_object('key','value2');
mysql> select json_merge_preserve(@a,@b);
> {"key": ["value", "value2"]}

通过转化 JSON 值生成的字符串,字符集是 utf8mb4,排序规则是 utf8mb4_bin

mysql> SELECT CHARSET(@j), COLLATION(@j);
>  utf8mb4  |  utf8mb4_bin 

由于 utf8mb4_bin 是二进制排序规则,因此 JSON 值的比较区分大小写

mysql> SELECT JSON_ARRAY('x') = JSON_ARRAY('x'),JSON_ARRAY('x') = JSON_ARRAY('X');
> 1  |  0

区分大小写也适用于 JSON null,true 和 false 文字,他们必须始终以小写形式写入。只有小写字母时 JSON 才是 JSON 有效值。

mysql>  SELECT JSON_VALID('null'), JSON_VALID('Null'), JSON_VALID('NULL');
>  1	|	0	|	0

mysql>  SELECT CAST('null' AS JSON);
> null

mysql>  SELECT CAST('Null' AS JSON);
> Invalid JSON text in argument 1 to function cast_as_json: "Invalid value." at position 0.

JSON 是区分大小写的,而 SQL 是不区分的。下列值都可以成功被识别为 null。

mysql> SELECT ISNULL(null), ISNULL(Null), ISNULL(NULL);
> 1	|	1	|	1

当你想要讲引号字符( " 或 " )插入到 JSON 文档中,你需要使用 \ 转义字符。

mysql> CREATE TABLE facts (sentence JSON);
mysql> INSERT INTO facts VALUES
     >   (JSON_OBJECT("mascot", "Our mascot is a dolphin named \"Sakila\"."));

如果将值作为 JSON 对象文字传入,则此方法不起作用。如下示例,第一个 \" 的位置会被当做 Our ma...这个属性的结束位置,所以报了缺少 逗号 或 大括号 的错误。

mysql> INSERT INTO facts VALUES
     >   ('{"mascot": "Our mascot is a dolphin named \"Sakila\"."}');
>	> 3140 - Invalid JSON text: "Missing a comma or '}' after an object member." at position 43 in value for column 'facts.sentence'.

这里需要使用双反斜杠来使之生效

mysql> INSERT INTO facts VALUES
     >   ('{"mascot": "Our mascot is a dolphin named \\"Sakila\\"."}');

查询查看效果,可以看到两种方式的结果是一样的。

mysql>	SELECT * FROM facts;
>	{"mascot": "Our mascot is a dolphin named \"Sakila\"."}
	{"mascot": "Our mascot is a dolphin named \"Sakila\"."}

要使用键查找特定的值,可以使用 column-path 操作符 ->,可以看到查询结果是完整的 value 值,包括引号和转义符

mysql> select sentence->"$.mascot" from facts;
> "Our mascot is a dolphin named \"Sakila\"."

有时候我们只需要里面的字符串,可以使用 ->> 运算符来查询;这样查询到的结果就只是需要显示的值了。

mysql> select sentence->>"$.mascot" from facts;
>	Our mascot is a dolphin named "Sakila".

如果启动了 NO_BACKSLASH_ESCAPES 服务器 SQL 模式,则前面的插入对象的实例将无法正常工作。如果设置了此模式,则可以使用单个反斜杠而不是双反斜杠来插入 JSON 对象文字,并保留反斜杠。如果在执行插入时使用 JSON_OBJECT() 函数并设置了此模式,则必须替换单引号和双引号

mysql> INSERT INTO facts VALUES (JSON_OBJECT('mascot', 'Our mascot is a dolphin named "Sakila".'));

mysql>	SELECT * FROM facts;
>	{"mascot": "Our mascot is a dolphin named \"Sakila\"."}

JSON值的规范化,合并和自动包装

解析字符串并发现它是有效的 JSON 文档时,它也会进行规范化。比如 JSON 对象中不能有重复的 key,那么有重复 key 时,后面的 value 会覆盖前面的

mysql> SELECT JSON_OBJECT('key1', 1, 'key2', 'abc', 'key1', 'def');
>	{"key1": "def", "key2": "abc"}

将值插入 JSON 列时也会执行规范化

mysql>	CREATE TABLE t1 (c1 JSON);
mysql>	INSERT INTO t1 VALUES
('{"x": 17, "x": "red"}'),
('{"x": 17, "x": "red", "x": [3, 5, 7]}');

mysql>	SELECT c1 FROM t1;
>	{"x": "red"}
	{"x": [3, 5, 7]}

这种 “last duplicate key wins(最后重复秘钥获胜)”的规则由 RFC 7159 建议,并且大多数 JavaScript 解析器都支持这个规则。(Bug #86866,Bug #26369555)

在 8.0.3 之前的 MySQL 版本中,重复的 key value 会被丢弃。如在 5.7.26 版本的 MySQL 做上面同样的测试,结果就不一样

mysql> SELECT JSON_OBJECT('key1', 1, 'key2', 'abc', 'key1', 'def');
>	{"key1": 1, "key2": "abc"}

mysql>	CREATE TABLE t1 (c1 JSON);
mysql>	INSERT INTO t1 VALUES
('{"x": 17, "x": "red"}'),
('{"x": 17, "x": "red", "x": [3, 5, 7]}');

mysql>	SELECT c1 FROM t1;
>	{"x": 17}
	{"x": 17}

MySQL 还会丢弃原始 JSON 文档中键、值或元素之间的额外空格。为了使查找更有效,它还对 JSON 对象的键进行排序。目前在这两个版本中做的简单测试,排序结果都是一样的。当然官方文档上也有提示:此排序结果可能会发生变化,并且不保证在各个版本中保持一致。

mysql>	select JSON_OBJECT('key1','value1','key3','value3','key2','value2');
>	{"key1": "value1", "key2": "value2", "key3": "value3"}

合并 JSON 值

MySQL 中有三个合并方法:

**合并数组:**将多个数组合并成单个数组。

JSON_MERGE_PRESERVE() 通过将后一数组追加到前一数组末尾实现。

JSON_MERGE_PATCH() 将每个参数视为由单个元素组成的数组(因此它们的下标都是 0),然后应用 “last duplicate key wins” 规则取最后一个参数。

mysql> SELECT
    ->   JSON_MERGE_PRESERVE('[1, 2]', '["a", "b", "c"]', '[true, false]') AS Preserve,
    ->   JSON_MERGE_PATCH('[1, 2]', '["a", "b", "c"]', '[true, false]') AS Patch\G
*************************** 1. row ***************************
Preserve: [1, 2, "a", "b", "c", true, false]
   Patch: [true, false]

注意:方法中都允许有 null ,但是结果需要注意。JSON_MERGE_PRESERVE() 方法中,若作为参数的数组中有个 null 值,那最终结果中也会有个 null,方法不会去除 null 值和重复值。

mysql> SELECT JSON_MERGE_PRESERVE('[1, 2]', '["a", "b", "c"]','[true, false]','["a", null, "c"]') AS Preserve;
>	[1, 2, "a", "b", "c", true, false, "a", null, "c"]

但如果作为参数的数组本身就是 null,那合并结果也是 null

mysql>	SELECT JSON_MERGE_PRESERVE('[1, 2]', '["a", "b", "c"]',null,'[true, false]') AS Preserve;
>	Null

JSON_MERGE_PATCH() 方法由于只保留最后一个数组,所以 null 对它并没有什么影响,只有当最后一个参数为 null 时结果才为 null

mysql>	SELECT JSON_MERGE_PATCH('[1, 2]', '["a", "b", "c"]', '[true, false]',null,'["a", "b", "c"]',null) AS Patch;
>	Null

**合并对象:**将多个对象合并成单个对象

JSON_MERGE_PRESERVE():将多个对象的键值对组合成一个新对象,会将重复键的值组成一个数组(不会去除重复值和 null)赋给这个键。

JSON_MERGE_PATCH():将多个对象的键值对组合成一个新对象,重复键的值会用 last duplicate key wins(最后重复秘钥获胜)规则取最后一个。

mysql> SELECT JSON_MERGE_PRESERVE('{"a": 1, "b": 2}', '{"c": 3, "a": 1}', '{"c": 5, "d": 3}','{"d":null}') AS Preserve,
JSON_MERGE_PATCH('{"a": 3, "b": 2}', '{"c": 3, "a": 4}', '{"c": 5, "d": 3}') AS Patch\G
*************************** 1. row ***************************
Preserve: {"a": [1, 1], "b": 2, "c": [3, 5], "d": [3, null]}
   Patch: {"a": 4, "b": 2, "c": 5, "d": 3}

合并非阵列值:

在需要数组值的上下文中使用的非阵列值被自动包装:该值被 [ 和 ] 字符包围以将其转换为数组。在下列语句中,每个参数都自动包装为数组([1],[2])。然后按照 合并数组 的规则进行合并。

mysql> SELECT
	  ->   JSON_MERGE_PRESERVE('1', '2') AS Preserve,
	  ->   JSON_MERGE_PATCH('1', '2') AS Patch\G
*************************** 1. row ***************************
Preserve: [1, 2]
   Patch: 2

需要注意的是,参数必须是正确的 JSON 类型值。可以使用 JSON_TYPE()方法检查值是否正确。

mysql>	SELECT JSON_MERGE_PRESERVE('"a"', '"b"') AS Preserve, JSON_MERGE_PATCH('"a"', '"b"') AS Patch;
*************************** 1. row ***************************
Preserve: ["a", "b"]
   Patch: "b"
   
   
mysql>	SELECT JSON_MERGE_PRESERVE('a', 'b') AS Preserve, JSON_MERGE_PATCH('a', 'b') AS Patch;
>	Invalid JSON text in argument 1 to function json_merge_preserve: "Invalid value." at position 0.

**合并数组和对象的组合:**合并的参数中既有数组,又有对象

合并时会将对象自动包装为数组,然后按照 合并数组 的规则合并。

mysql> SELECT
	  ->   JSON_MERGE_PRESERVE('[10, 20]', '{"a": "x", "b": "y"}') AS Preserve,
	  ->   JSON_MERGE_PATCH('[10, 20]', '{"a": "x", "b": "y"}') AS Patch\G
*************************** 1. row ***************************
Preserve: [10, 20, {"a": "x", "b": "y"}]
   Patch: {"a": "x", "b": "y"}

查询和修改 JSON 值

JSON 路径表达式选择 JSON 文档中的值。

路径表达式对于提取 JSON 文档的一部分或修改 JSON 文档的函数很有用,以指定该文档中的操作位置。

以下查询从 JSON 文档中提取名称为 key 的成员的值:

mysql> SELECT JSON_EXTRACT('{"id": 14, "name": "Aztalan"}', '$.name');
>	 "Aztalan"   

路径语法使用前导 $ 字符来表示正在计算的 JSON 文档,后面可以跟选择器,来连续指示文档更具体的部分:

mysql>	SELECT JSON_SET('"x"', '$[0]', 'a');
>	"a"

让 $ 引用这个带有三个元素的 JSON 数组:

[3, {"a" : [5, 6], "b" : 10}, [99, 100]]

然后:

因为 [1] 和 [2] 的值为非标量值,所以它们可以用作更具体的路径表达式的基础来选择嵌套的值

如前所述,如果路径表达式中的未加引号的键名称不合法,则必须引用命名键的路径组件。让 $ 引用这个值

{"a fish": "shark", "a bird": "sparrow"}

key 都包含空格,必须引用:

使用通配符的路径计算的源数据可以是一个包含多个值的数组

mysql> SELECT JSON_EXTRACT('{"a": 1, "b": 2, "c": [3, 4, 5]}', '$.*');
>	 [1, 2, [3, 4, 5]]  

mysql> SELECT JSON_EXTRACT('{"a": 1, "b": 2, "c": [3, 4, 5]}', '$.c[*]');
>	 [3, 4, 5]       

来自 JSON 数组的范围。可以使用带有 to 关键字的范围来指定 JSON 数组的子集。

mysql> SELECT JSON_EXTRACT('[1, 2, 3, 4, 5]', '$[last-3 to last-1]');
>	[2, 3, 4] 

如果针对不是数组的值计算路径,则评估结果与将值包装在单个元素数组中的结果相同。替换指定值,因为[0] 和 [last] 都指向 “Sakila”,所以该值被替换为 10,返回结果为 10;$[其它值] 找不到可替换的值,所以最终返回原值

mysql>	SELECT JSON_REPLACE('"Sakila"', '$[0]',10) zero, JSON_REPLACE('"Sakila"', '$[last]',10) last, JSON_REPLACE('"Sakila"', '$[1]',10) other;
*************************** 1. row ***************************
zero: 10
last: 10
other: "Sakila"

你可以将 column-> path 与 JSON 列标识符和 JSON 路径表达式一起用作 JSON_EXTRACT (列,路径)的同义词。

某些函数采用现有的 JSON 文档,以某种方式对其进行修改,并返回生成的修改后的文档。路径表达式指示文档中的更改位置。例如:JSON_SET(), JSON_INSERT(), JSON_REPLACE() 函数各自采用 JSON 文档,以及一个或多个路径值对,这些路径值对描述了修改文档的位置和要使用的值。

这些函数在处理文档中的现有值和不存在值方面有所不同。

考虑这个文档:

mysql> SET @j = '["a", {"b": [true, false]}, [10, 20]]';

JSON_SET() 替换存在的路径的值,并为不存在的路径添加值。[1].b[0] 原本的true被替换为1, [2][2] 原本是不存在的,现在新增了个 2

mysql> SELECT JSON_SET(@j, '$[1].b[0]', 1, '$[2][2]', 2);
>  ["a", {"b": [1, false]}, [10, 20, 2]]

JSON_INSERT() 添加新值但不替换现有值

mysql> SELECT JSON_INSERT(@j, '$[1].b[0]', 1, '$[2][2]', 2);
>	 ["a", {"b": [true, false]}, [10, 20, 2]]

JSON_REPLACE() 替换现有的值但忽略新值

mysql> SELECT JSON_REPLACE(@j, '$[1].b[0]', 1, '$[2][2]', 2);
>  ["a", {"b": [1, false]}, [10, 20]]  

路径值对从左到右进行评估。通过评估一对产生的文档成为下一个评估的源值。(类似于递归)

JSON_RMOVE() 接受一个 JSON 文档和一个或多个指定要从文档中删除的值的路径。返回值是原始文档减去文档中存在的路径选择的值:

mysql> SELECT JSON_REMOVE(@j, '$[2]', '$[1].b[1]', '$[1].b[1]');
> ["a", {"b": [true]}]   

JSON路径语法

MySQL 中的很多 JSON 函数需要路径表达式来标识 JSON 文档中的特定元素。一个路径由路径范围后跟一个或多个分支路径组成。对于 MySQL JSON 函数使用的路径,范围始终是正在搜索或以其它方式操作的文档,由前导 $ 字符表示。路径分支用 . 字符分隔。数组中的元素用 [N] 表示,其中 N 是非负整数。秘钥的名称必须是双引号字符串或有效的 ECMAScript 标识符(请参阅http://www.ecma-international.org/ecma-262/5.1/#sec-7.6)。路径表达式(如 JSON 文本)应使用 ascii,utf8 或 utf8mb4 字符集进行编码。其它字符编码被隐式强行转换为 utf8mb4。

pathExpression:
    scope[(pathLeg)*]

pathLeg:
    member | arrayLocation | doubleAsterisk

member:
    period ( keyName | asterisk )

arrayLocation:
    leftBracket ( nonNegativeInteger | asterisk ) rightBracket

keyName:
    ESIdentifier | doubleQuotedString

doubleAsterisk:
    '**'

period:
    '.'

asterisk:
    '*'

leftBracket:
    '['

rightBracket:
    ']'

如上所述,在 MySQL 中,路径的范围始终是正在操作的文档,表示为。你可以使用 ‘’ 字符作为 JSON 路径表达式中文档的别名。

JSON值的比较和排序

JSON 值可以使用 =, <, <=, >, >=, <>, != 和 <=> 运算符进行比较。

其中

<=> : NULL 安全相等。此运算符执行与 = 号运算符类似的相等比较,但如果两个操作数均为 NULL,则返回 1 而不是 NULL;如果一个操作数为 NULL,则返回 0 而不是 NULL。

<=> 等同于标准 SQL 中 is not distinct from 运算符

mysql> SELECT 1 <=> 1, NULL <=> NULL, 1 <=> NULL;
        -> 1, 1, 0
mysql> SELECT 1 = 1, NULL = NULL, 1 = NULL;
        -> 1, NULL, NULL

对于行比较,(a, b) <=> (x, y) 等效于 (a <=> x) AND (b <=> y)

JSON 值尚不支持以下比较运算符和函数:

为了使用上述列出的比较运算符和函数,可以将 JSON 值转换为本机 MySQL 数值或字符串数据类型,以便它们具有一致的非 JSON 标量类型。

JSON 值的比较发生在两个级别。

第一级比较基于比较值的 JSON 类型。如果类型不同,则比较结果仅由哪种类型具有更高优先级来确定。

如果这两个值具有相同的 JSON 类型,则使用特定于类型的规则进行第二级比较。

下面列出了 JSON 类型的优先级,从最高优先级到最低优先级。(类型名称是 JSON_TYPE()函数返回的类型名称。)同一行显示的类型优先级相同。列表中前面列出的任何具有 JSON 类型的值都比列表中稍后列出的具有 JSON 类型的任何值都要大。

BLOB
BIT
OPAQUE
DATETIME
TIME
DATE
BOOLEAN
ARRAY
OBJECT
STRING
INTEGER, DOUBLE
NULL

对于具有相同优先级的 JSON 值,比较规则是特定于类型的:

任何 JSON 值与 SQL 的 NULL 值比较,结果都是 UNKNOWN。

为了比较 JSON 值和非 JSON 值,非 JSON 值会按照下列规则转化为 JSON 值,然后按照之前提到的规则做比较。

JSON值和非JSON值相互转换

下表提供了 MySQL 在 JSON 值和其它类型值之间进行转换时遵循的规则的摘要:

其它类型 CAST(其它类型转换为 JSON) CAST(JSON 转换为其它类型)
JSON 不改变 不改变
utf8 character type (utf8mb4,utf8, ascii) 字符串解析为 JSON 值。 JSON 值被序列化为 utf8mb4 字符串.
Other character types 其它字符编码隐式转换为 utf8mb4,并按照 utf8 字符类型的描述进行处理. JSON 值被序列化为 utf8mb4 字符串,然后转换为其它字符编码。结果可能没有意义(乱码).
NULL 结果是值为 NULL 的 JSON. 不可用.
Geometry types The geometry value is converted into a JSON document by callingST_AsGeoJSON(). Illegal operation. Workaround: Pass the result of CAST(*json_val*AS CHAR) to ST_GeomFromGeoJSON().
All other types Results in a JSON document consisting of a single scalar value. Succeeds if the JSON document consists of a single scalar value of the target type and that scalar value can be cast to the target type. Otherwise, returns NULL and produces a warning.

JSON 值的 ORDER BY 和 GROUP BY 按照以下原则工作:

对于排序,将 JSON 标量转换为其它一些本地 MySQL 类型可能是有益的。例如,如果名为 jdoc 的列包含具有由 id 键和非负值组成的成员的 JSON 对象,请使用此表达式按 id 值排序:

ORDER BY CAST(JSON_EXTRACT(jdoc, '$.id') AS UNSIGNED)

如果确定生成的列定义为使用与 ORDER BY 中相同的表达式,则 MySQL 优化器会识别并考虑使用索引执行查询计划。

JSON值的聚合

对于 JSON 值的聚合,SQL NULL 会像其它数据类型一样被忽略。非空值转换为数字类型并聚合,但 MIN(), MAX(), GROUP_CONCAT() 除外。转换为数字应该会为 JSON 值产生一个有意义的结果,这些值是数值标量,尽管(取决于值)可能会发生截断和精度损失。转换为的数字的 JSON 值也可能是没有意义的结果。

  • MySQL

    MySQL 是一个关系型数据库管理系统,由瑞典 MySQL AB 公司开发,目前属于 Oracle 公司。MySQL 是最流行的关系型数据库管理系统之一。

    452 引用 • 468 回帖 • 865 关注
  • JSON

    JSON (JavaScript Object Notation)是一种轻量级的数据交换格式。易于人类阅读和编写。同时也易于机器解析和生成。

    38 引用 • 161 回帖
回帖
请输入回帖内容...