> ## Documentation Index
> Fetch the complete documentation index at: https://clickhouse.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# 处理其他 JSON 格式

> 处理其他 JSON 格式

前文的 JSON 数据加载示例都默认使用 [`JSONEachRow`](/docs/zh/reference/formats/JSON/JSONEachRow) (`NDJSON`) 。这种格式会将每个 JSON 行中的键读取为列。例如：

```sql theme={null}
SELECT *
FROM s3('https://datasets-documentation.s3.eu-west-3.amazonaws.com/pypi/json/*.json.gz', JSONEachRow)
LIMIT 5
```

```response theme={null}
┌───────date─┬─country_code─┬─project────────────┬─type────────┬─installer────┬─python_minor─┬─system─┬─version─┐
│ 2022-11-15 │ CN           │ clickhouse-connect │ bdist_wheel │ bandersnatch │              │        │ 0.2.8   │
│ 2022-11-15 │ CN           │ clickhouse-connect │ bdist_wheel │ bandersnatch │              │        │ 0.2.8   │
│ 2022-11-15 │ CN           │ clickhouse-connect │ bdist_wheel │ bandersnatch │              │        │ 0.2.8   │
│ 2022-11-15 │ CN           │ clickhouse-connect │ bdist_wheel │ bandersnatch │              │        │ 0.2.8   │
│ 2022-11-15 │ CN           │ clickhouse-connect │ bdist_wheel │ bandersnatch │              │        │ 0.2.8   │
└────────────┴──────────────┴────────────────────┴─────────────┴──────────────┴──────────────┴────────┴─────────┘

5 rows in set. Elapsed: 0.449 sec.
```

虽然这通常是最常用的 JSON 格式，但你也会遇到其他格式，或者需要将 JSON 作为单个对象来读取。

下面我们提供了一些示例，说明如何以其他常见格式读取和加载 JSON。

<div id="reading-json-as-an-object">
  ## 将 JSON 作为对象读取
</div>

前面的示例展示了 `JSONEachRow` 如何读取以换行分隔的 JSON：每一行都会作为单独的对象读取，映射到表中的一行，而每个键则映射到一列。这非常适合 JSON 结构可预测、且每列只有单一类型的场景。

相比之下，`JSONAsObject` 会将每一行视为一个单独的 `JSON` 对象，并将其存储在单个列中，类型为 [`JSON`](/docs/zh/reference/data-types/newjson)，因此更适合嵌套的 JSON 载荷，以及键是动态的、且可能对应多种类型的场景。

对于按行 insert，请使用 `JSONEachRow`；而在存储灵活或动态的 JSON 数据时，请使用 [`JSONAsObject`](/docs/zh/reference/formats/JSON/JSONAsObject)。

对比上面的示例和下面这个查询：后者会将相同的数据按每行一个 JSON 对象的方式读取：

```sql theme={null}
SELECT *
FROM s3('https://datasets-documentation.s3.eu-west-3.amazonaws.com/pypi/json/*.json.gz', JSONAsObject)
LIMIT 5
```

```response theme={null}
┌─json─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ {"country_code":"CN","date":"2022-11-15","installer":"bandersnatch","project":"clickhouse-connect","python_minor":"","system":"","type":"bdist_wheel","version":"0.2.8"} │
│ {"country_code":"CN","date":"2022-11-15","installer":"bandersnatch","project":"clickhouse-connect","python_minor":"","system":"","type":"bdist_wheel","version":"0.2.8"} │
│ {"country_code":"CN","date":"2022-11-15","installer":"bandersnatch","project":"clickhouse-connect","python_minor":"","system":"","type":"bdist_wheel","version":"0.2.8"} │
│ {"country_code":"CN","date":"2022-11-15","installer":"bandersnatch","project":"clickhouse-connect","python_minor":"","system":"","type":"bdist_wheel","version":"0.2.8"} │
│ {"country_code":"CN","date":"2022-11-15","installer":"bandersnatch","project":"clickhouse-connect","python_minor":"","system":"","type":"bdist_wheel","version":"0.2.8"} │
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

5 rows in set. Elapsed: 0.338 sec.
```

`JSONAsObject` 可用于通过单个 JSON 对象列向表中插入行，例如：

```sql theme={null}
CREATE TABLE pypi
(
    `json` JSON
)
ENGINE = MergeTree
ORDER BY tuple();

INSERT INTO pypi SELECT *
FROM s3('https://datasets-documentation.s3.eu-west-3.amazonaws.com/pypi/json/*.json.gz', JSONAsObject)
LIMIT 5;

SELECT *
FROM pypi
LIMIT 2;
```

```response theme={null}
┌─json─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ {"country_code":"CN","date":"2022-11-15","installer":"bandersnatch","project":"clickhouse-connect","python_minor":"","system":"","type":"bdist_wheel","version":"0.2.8"} │
│ {"country_code":"CN","date":"2022-11-15","installer":"bandersnatch","project":"clickhouse-connect","python_minor":"","system":"","type":"bdist_wheel","version":"0.2.8"} │
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

2 rows in set. Elapsed: 0.003 sec.
```

在对象结构不一致的情况下，`JSONAsObject` 格式对于读取以换行分隔的 JSON 也很有用。例如，如果某个键在不同行中的类型会发生变化 (有时是字符串，有时又是对象) 。在这种情况下，ClickHouse 无法使用 `JSONEachRow` 推断出稳定的 schema，而 `JSONAsObject` 则允许在不强制执行严格类型检查的情况下摄取数据，将每个 JSON 行作为整体存储在单个列中。例如，请看 `JSONEachRow` 如何在下面的示例中失败：

```sql theme={null}
SELECT count()
FROM s3('https://clickhouse-public-datasets.s3.amazonaws.com/bluesky/file_0001.json.gz', 'JSONEachRow')
```

```response theme={null}
Elapsed: 1.198 sec.

Received exception from server (version 24.12.1):
Code: 636. DB::Exception: Received from sql-clickhouse.clickhouse.com:9440. DB::Exception: The table structure cannot be extracted from a JSONEachRow format file. Error:
Code: 117. DB::Exception: JSON objects have ambiguous data: in some objects path 'record.subject' has type 'String' and in some - 'Tuple(`$type` String, cid String, uri String)'. You can enable setting input_format_json_use_string_type_for_ambiguous_paths_in_named_tuples_inference_from_objects to use String type for path 'record.subject'. (INCORRECT_DATA) (version 24.12.1.18239 (official build))
To increase the maximum number of rows/bytes to read for structure determination, use setting input_format_max_rows_to_read_for_schema_inference/input_format_max_bytes_to_read_for_schema_inference.
You can specify the structure manually: (in file/uri bluesky/file_0001.json.gz). (CANNOT_EXTRACT_TABLE_STRUCTURE)
```

相反，在这种情况下可以使用 `JSONAsObject`，因为 `JSON` 类型允许同一子列对应多种类型。

```sql theme={null}
SELECT count()
FROM s3('https://clickhouse-public-datasets.s3.amazonaws.com/bluesky/file_0001.json.gz', 'JSONAsObject')
```

```response theme={null}
┌─count()─┐
│ 1000000 │
└─────────┘

1 row in set. Elapsed: 0.480 sec. Processed 1.00 million rows, 256.00 B (2.08 million rows/s., 533.76 B/s.)
```

<div id="array-of-json-objects">
  ## JSON 对象数组
</div>

JSON 数据最常见的一种形式，是在 JSON 数组中包含一组 JSON 对象，如[此示例](https://clickhouse-docs-assets.s3.us-east-1.amazonaws.com/list.json)所示：

```bash theme={null}
> cat list.json
[
  {
    "path": "Akiba_Hebrew_Academy",
    "month": "2017-08-01",
    "hits": 241
  },
  {
    "path": "Aegithina_tiphia",
    "month": "2018-02-01",
    "hits": 34
  },
  ...
]
```

下面为这类数据创建一个表：

```sql theme={null}
CREATE TABLE sometable
(
    `path` String,
    `month` Date,
    `hits` UInt32
)
ENGINE = MergeTree
ORDER BY tuple(month, path)
```

要导入一组 JSON 对象，可以使用 [`JSONEachRow`](/docs/zh/reference/formats/JSON/JSONEachRow) 格式 (从 [list.json](https://clickhouse-docs-assets.s3.us-east-1.amazonaws.com/list.json) 文件中插入数据) ：

```sql theme={null}
INSERT INTO sometable
FROM INFILE 'list.json'
FORMAT JSONEachRow
```

我们使用了 [FROM INFILE](/docs/zh/reference/statements/insert-into#inserting-data-from-a-file) 子句从本地文件加载数据，可以看到导入已成功：

```sql theme={null}
SELECT *
FROM sometable
```

```response theme={null}
┌─path──────────────────────┬──────month─┬─hits─┐
│ 1971-72_Utah_Stars_season │ 2016-10-01 │    1 │
│ Akiba_Hebrew_Academy      │ 2017-08-01 │  241 │
│ Aegithina_tiphia          │ 2018-02-01 │   34 │
└───────────────────────────┴────────────┴──────┘
```

<div id="json-object-keys">
  ## JSON 对象键
</div>

在某些情况下，JSON 对象列表也可以编码为对象属性，而不是数组元素 (示例见 [objects.json](https://clickhouse-docs-assets.s3.us-east-1.amazonaws.com/objects.json)) ：

```bash theme={null}
cat objects.json
```

```response theme={null}
{
  "a": {
    "path":"April_25,_2017",
    "month":"2018-01-01",
    "hits":2
  },
  "b": {
    "path":"Akahori_Station",
    "month":"2016-06-01",
    "hits":11
  },
  ...
}
```

ClickHouse 可以使用 [`JSONObjectEachRow`](/docs/zh/reference/formats/JSON/JSONObjectEachRow) 格式从此类数据中导入数据：

```sql theme={null}
INSERT INTO sometable FROM INFILE 'objects.json' FORMAT JSONObjectEachRow;
SELECT * FROM sometable;
```

```response theme={null}
┌─path────────────┬──────month─┬─hits─┐
│ Abducens_palsy  │ 2016-05-01 │   28 │
│ Akahori_Station │ 2016-06-01 │   11 │
│ April_25,_2017  │ 2018-01-01 │    2 │
└─────────────────┴────────────┴──────┘
```

<div id="specifying-parent-object-key-values">
  ### 指定父对象的键值
</div>

假设我们还想将父对象键中的值保存到表中。在这种情况下，我们可以使用[以下选项](/docs/zh/reference/settings/formats#format_json_object_each_row_column_for_object_name)来指定用于保存键值的列名：

```sql theme={null}
SET format_json_object_each_row_column_for_object_name = 'id'
```

现在，我们可以使用 [`file()`](/docs/zh/reference/functions/regular-functions/files#file) 函数查看将从原始 JSON 文件加载哪些数据：

```sql theme={null}
SELECT * FROM file('objects.json', JSONObjectEachRow)
```

```response theme={null}
┌─id─┬─path────────────┬──────month─┬─hits─┐
│ a  │ April_25,_2017  │ 2018-01-01 │    2 │
│ b  │ Akahori_Station │ 2016-06-01 │   11 │
│ c  │ Abducens_palsy  │ 2016-05-01 │   28 │
└────┴─────────────────┴────────────┴──────┘
```

请注意，`id` 列已正确填入键值。

<div id="json-arrays">
  ## JSON 数组
</div>

有时，为了节省空间，JSON 文件会采用数组而不是对象的编码方式。在这种情况下，我们处理的是一份 [JSON 数组列表](https://clickhouse-docs-assets.s3.us-east-1.amazonaws.com/arrays.jsonl)：

```bash theme={null}
cat arrays.json
```

```response theme={null}
["Akiba_Hebrew_Academy", "2017-08-01", 241],
["Aegithina_tiphia", "2018-02-01", 34],
["1971-72_Utah_Stars_season", "2016-10-01", 1]
```

在这种情况下，ClickHouse 会加载这些数据，并根据数组中的顺序将每个值对应到相应的列。为此，我们使用 [`JSONCompactEachRow`](/docs/zh/reference/formats/JSON/JSONCompactEachRow) 格式：

```sql theme={null}
SELECT * FROM sometable
```

```response theme={null}
┌─c1────────────────────────┬─────────c2─┬──c3─┐
│ Akiba_Hebrew_Academy      │ 2017-08-01 │ 241 │
│ Aegithina_tiphia          │ 2018-02-01 │  34 │
│ 1971-72_Utah_Stars_season │ 2016-10-01 │   1 │
└───────────────────────────┴────────────┴─────┘
```

<div id="importing-individual-columns-from-json-arrays">
  ### 从 JSON 数组中导入各个列
</div>

在某些情况下，数据可以按列而非按行编码。在这种情况下，外层 JSON 对象包含各列及其值。请查看[以下文件](https://clickhouse-docs-assets.s3.us-east-1.amazonaws.com/columns.json)：

```bash theme={null}
cat columns.json
```

```response theme={null}
{
  "path": ["2007_Copa_America", "Car_dealerships_in_the_USA", "Dihydromyricetin_reductase"],
  "month": ["2016-07-01", "2015-07-01", "2015-07-01"],
  "hits": [178, 11, 1]
}
```

ClickHouse 使用 [`JSONColumns`](/docs/zh/reference/formats/JSON/JSONColumns) 格式来解析如下格式的数据：

```sql theme={null}
SELECT * FROM file('columns.json', JSONColumns)
```

```response theme={null}
┌─path───────────────────────┬──────month─┬─hits─┐
│ 2007_Copa_America          │ 2016-07-01 │  178 │
│ Car_dealerships_in_the_USA │ 2015-07-01 │   11 │
│ Dihydromyricetin_reductase │ 2015-07-01 │    1 │
└────────────────────────────┴────────────┴──────┘
```

如果处理的是[列数组](https://clickhouse-docs-assets.s3.us-east-1.amazonaws.com/columns-array.json)而不是对象，也支持使用更紧凑的 [`JSONCompactColumns`](/docs/zh/reference/formats/JSON/JSONCompactColumns) 格式：

```sql theme={null}
SELECT * FROM file('columns-array.json', JSONCompactColumns)
```

```response theme={null}
┌─c1──────────────┬─────────c2─┬─c3─┐
│ Heidenrod       │ 2017-01-01 │ 10 │
│ Arthur_Henrique │ 2016-11-01 │ 12 │
│ Alan_Ebnother   │ 2015-11-01 │ 66 │
└─────────────────┴────────────┴────┘
```

<div id="saving-json-objects-instead-of-parsing">
  ## 保存 JSON 对象而不解析
</div>

在某些情况下，你可能希望将 JSON 对象保存到单个 `String` (或 `JSON`) 列中，而不是对其进行解析。在处理由不同结构的 JSON 对象组成的列表时，这会很有用。以[这个文件](https://clickhouse-docs-assets.s3.us-east-1.amazonaws.com/custom.json)为例，其中父列表内包含多个不同的 JSON 对象：

```bash theme={null}
cat custom.json
```

```response theme={null}
[
  {"name": "Joe", "age": 99, "type": "person"},
  {"url": "/my.post.MD", "hits": 1263, "type": "post"},
  {"message": "Warning on disk usage", "type": "log"}
]
```

我们希望将原始 JSON 对象保存到下列表中：

```sql theme={null}
CREATE TABLE events
(
    `data` String
)
ENGINE = MergeTree
ORDER BY ()
```

现在，我们可以使用 [`JSONAsString`](/docs/zh/reference/formats/JSON/JSONAsString) 格式将文件中的数据加载到该表中，从而保留 JSON 对象，而不对其进行解析：

```sql theme={null}
INSERT INTO events (data)
FROM INFILE 'custom.json'
FORMAT JSONAsString
```

我们可以使用 [JSON functions](/docs/zh/reference/functions/regular-functions/json-functions) 对已保存的对象进行查询：

```sql theme={null}
SELECT
    JSONExtractString(data, 'type') AS type,
    data
FROM events
```

```response theme={null}
┌─type───┬─data─────────────────────────────────────────────────┐
│ person │ {"name": "Joe", "age": 99, "type": "person"}         │
│ post   │ {"url": "/my.post.MD", "hits": 1263, "type": "post"} │
│ log    │ {"message": "Warning on disk usage", "type": "log"}  │
└────────┴──────────────────────────────────────────────────────┘
```

请注意，对于按每行一个 JSON 对象组织的文件 (通常与 `JSONEachRow` 格式一起使用) ，`JSONAsString` 也完全可以正常工作。

<div id="schema-for-nested-objects">
  ## 嵌套对象的 schema
</div>

在处理[嵌套 JSON 对象](https://clickhouse-docs-assets.s3.us-east-1.amazonaws.com/list-nested.json)时，我们还可以显式定义 schema，并使用复杂类型 ([`Array`](/docs/zh/reference/data-types/array)、[`JSON`](/docs/zh/guides/clickhouse/data-formats/json/intro) 或 [`Tuple`](/docs/zh/reference/data-types/tuple)) 来加载数据：

```sql theme={null}
SELECT *
FROM file('list-nested.json', JSONEachRow, 'page Tuple(path String, title String, owner_id UInt16), month Date, hits UInt32')
LIMIT 1
```

```response theme={null}
┌─page───────────────────────────────────────────────┬──────month─┬─hits─┐
│ ('Akiba_Hebrew_Academy','Akiba Hebrew Academy',12) │ 2017-08-01 │  241 │
└────────────────────────────────────────────────────┴────────────┴──────┘
```

<div id="accessing-nested-json-objects">
  ## 访问嵌套 JSON 对象
</div>

启用[以下设置选项](/docs/zh/reference/settings/formats#input_format_import_nested_json)后，我们就可以引用[嵌套 JSON 键](https://clickhouse-docs-assets.s3.us-east-1.amazonaws.com/list-nested.json)：

```sql theme={null}
SET input_format_import_nested_json = 1
```

这样我们就可以使用点表示法来引用嵌套 JSON 对象的键 (注意要用反引号将这些键括起来才能生效) ：

```sql theme={null}
SELECT *
FROM file('list-nested.json', JSONEachRow, '`page.owner_id` UInt32, `page.title` String, month Date, hits UInt32')
LIMIT 1
```

```results theme={null}
┌─page.owner_id─┬─page.title───────────┬──────month─┬─hits─┐
│            12 │ Akiba Hebrew Academy │ 2017-08-01 │  241 │
└───────────────┴──────────────────────┴────────────┴──────┘
```

这样，我们就可以将嵌套的 JSON 对象展平，或者提取其中的一些嵌套值，将其另存为单独的列。

<div id="skipping-unknown-columns">
  ## 跳过未知列
</div>

默认情况下，ClickHouse 在导入 JSON 数据时会忽略未知列。现在尝试将原始文件导入不包含 `month` 列的表中：

```sql theme={null}
CREATE TABLE shorttable
(
    `path` String,
    `hits` UInt32
)
ENGINE = MergeTree
ORDER BY path
```

我们仍然可以将包含 3 列的[原始 JSON 数据](https://clickhouse-docs-assets.s3.us-east-1.amazonaws.com/list.json)插入这个表中：

```sql theme={null}
INSERT INTO shorttable FROM INFILE 'list.json' FORMAT JSONEachRow;
SELECT * FROM shorttable
```

```response theme={null}
┌─path──────────────────────┬─hits─┐
│ 1971-72_Utah_Stars_season │    1 │
│ Aegithina_tiphia          │   34 │
│ Akiba_Hebrew_Academy      │  241 │
└───────────────────────────┴──────┘
```

ClickHouse 在导入时会忽略未知列。可通过 [input\_format\_skip\_unknown\_fields](/docs/zh/reference/settings/formats#input_format_skip_unknown_fields) 设置项禁用此行为：

```sql theme={null}
SET input_format_skip_unknown_fields = 0;
INSERT INTO shorttable FROM INFILE 'list.json' FORMAT JSONEachRow;
```

```response theme={null}
Ok.
Exception on client:
Code: 117. DB::Exception: Unknown field found while parsing JSONEachRow format: month: (in file/uri /data/clickhouse/user_files/list.json): (at row 1)
```

当 JSON 与表列结构不一致时，ClickHouse 会抛出异常。

<div id="bson">
  ## BSON
</div>

ClickHouse 支持将数据导出为 [BSON](https://bsonspec.org/) 编码文件，也支持从这类文件导入数据。一些 DBMS 会使用这种格式，例如 [MongoDB](https://github.com/mongodb/mongo) 数据库。

要导入 BSON 数据，我们使用 [BSONEachRow](/docs/zh/reference/formats/BSONEachRow) 格式。下面从[这个 BSON 文件](https://clickhouse-docs-assets.s3.us-east-1.amazonaws.com/data.bson)导入数据：

```sql theme={null}
SELECT * FROM file('data.bson', BSONEachRow)
```

```response theme={null}
┌─path──────────────────────┬─month─┬─hits─┐
│ Bob_Dolman                │ 17106 │  245 │
│ 1-krona                   │ 17167 │    4 │
│ Ahmadabad-e_Kalij-e_Sofla │ 17167 │    3 │
└───────────────────────────┴───────┴──────┘
```

我们也可以使用相同的格式导出到 BSON 文件：

```sql theme={null}
SELECT *
FROM sometable
INTO OUTFILE 'out.bson'
FORMAT BSONEachRow
```

之后，我们的数据会导出到 `out.bson` 文件中。
