JsonTreeReader Controller Service
描述
将 JSON 解析为单独的 Record 对象。 虽然读者期望每条记录都是格式良好的 JSON,但 FlowFile 的内容可能由许多记录组成,每个记录都是格式良好的 JSON 数组或 JSON 对象,它们之间有可选的空格,例如常见的“每行 JSON”格式。 如果遇到数组,则该数组中的每个元素将被视为单独的记录。 如果配置的架构包含 JSON 中不存在的字段,则将使用 null 值。 如果 JSON 包含架构中不存在的字段,则该字段将被跳过。 有关更多信息和示例,请参阅Controller Service的使用。
标签
属性
| 名称 | 默认值 | 可选值 | 描述 |
|---|---|---|---|
| Schema Access Strategy | infer-schema |
| 指定如何获取用于解释数据的架构。 |
| Schema Registry | | 控制器服务 API:SchemaRegistrycn.org.acal.lynxus-lynxus-standard-services-api-nar-2.11.8 | 指定用于架构Registry的Controller Service |
| Schema Name | ${schema.name} | 指定要在架构Registry属性中查找的架构名称 支持表达式语言:是(使用 FlowFile 属性与环境/系统变量求值) | |
| Schema Version | | 指定在 Schema Registry 中查找 Schema 的版本。如果未指定,则检索 Schema 的最新版本。 支持表达式语言:是(使用 FlowFile 属性与环境/系统变量求值) | |
| Schema Branch | | 指定在 Schema Registry 属性中查找 Schema 时使用的分支名称。如果选择的 Schema Registry 不支持分支,则此值将被忽略。 支持表达式语言:是(使用 FlowFile 属性与环境/系统变量求值) | |
| Schema Text | ${avro.schema} | Avro 格式的架构文本 支持表达式语言:是(使用 FlowFile 属性与环境/系统变量求值) | |
| Schema Reference Reader | | 控制器服务 API:SchemaReferenceReadercn.org.acal.lynxus-lynxus-standard-services-api-nar-2.11.8 | 服务实现负责读取 FlowFile 属性或内容以确定架构参考标识符 |
| Schema Inference Cache | | 控制器服务 API:RecordSchemaCacheServicecn.org.acal.lynxus-lynxus-standard-services-api-nar-2.11.8 | 指定在推断 Schema 时使用的 Schema 缓存。如果未填充缓存,每次都会推断 Schema。但是,如果指定了缓存,将首先查询缓存,如果能找到适用的 Schema,则使用该 Schema 而不是推断。 |
| Starting Field Strategy | ROOT_NODE |
| 从根节点或指定的嵌套节点开始处理。 |
| Starting Field Name | | 向前跳到给定的嵌套字段(数组或对象)以开始处理。 | |
| Schema Application Strategy | SELECTED_PART |
| 指定 Schema 是为整个文档定义的,还是为从 "Starting Field Name" 开始的部分定义的。 |
| Max String Length | 20 MB | 解析文档时字符串值允许的最大长度 | |
| Parsing Strategy | STANDARD |
| 设置所需的 JSON 规范符合级别的策略 |
| Date Format | | 指定读取/写入日期字段时使用的格式。如果未指定,日期字段将被视为自纪元(1970 年 1 月 1 日午夜 GMT)以来的毫秒数。如果指定,值必须匹配 Java java.time.format.DateTimeFormatter 格式(例如,MM/dd/yyyy 表示两位数字的月份,后跟两位数字的日期,后跟四位数字的年份,全部以 '/' 字符分隔,如 01/01/2017)。 | |
| Time Format | | 指定读取/写入时间字段时使用的格式。如果未指定,时间字段将被视为自纪元(1970 年 1 月 1 日午夜 GMT)以来的毫秒数。如果指定,值必须匹配 Java java.time.format.DateTimeFormatter 格式(例如,HH:mm:ss 表示 24 小时格式的两位数字的小时,后跟两位数字的分钟,后跟两位数字的秒,全部以 ':' 字符分隔,如 18:04:15)。 | |
| Timestamp Format | | 指定读取/写入时间戳字段时使用的格式。如果未指定,时间戳字段将被视为自纪元(1970 年 1 月 1 日午夜 GMT)以来的毫秒数。如果指定,值必须匹配 Java java.time.format.DateTimeFormatter 格式(例如,MM/dd/yyyy HH:mm:ss 表示两位数字的月份,后跟两位数字的日期,后跟四位数字的年份,全部以 '/' 字符分隔;然后后跟 24 小时格式的两位数字的小时,后跟两位数字的分钟,后跟两位数字的秒,全部以 ':' 字符分隔,如 01/01/2017 18:04:15)。 |
状态管理
该组件不存储状态。
受限 (Restricted)
该组件不受限。
提供的 Service API
RecordReaderFactory
另请参阅
详细说明
JsonTreeReader
JsonTreeReader Controller Service 读取 JSON 对象,并为整个 JSON 对象树或 其中的一部分(参见"起始字段策略"一节)创建 Record 对象。必须为该 Controller Service 配置一个描述 JSON 数据结构的 Schema。如果 JSON 中存在 schema 中没有的字段,该字段会被跳过。如果 schema 中存在某个字段而 JSON 中没有对应字段,记录中会使用 null 值(如适用,也可使用 schema 定义的 默认值)。
如果 JSON 的根元素是 JSON 数组,数组中的每个 JSON 对象会被视为一条独立的 记录。如果根元素是 JSON 对象,整个 JSON 会被视为单条记录。
Schema 与类型强制转换
从传入数据解析记录时,数据会被拆分为各个字段。每个字段随后会按字段名与 配置的 schema 进行比对,以确定数据的类型。如果字段不在 schema 中,该字段 会从记录中省略。如果在 schema 中找到了该字段,接收到的数据的数据类型会 与 schema 指定的数据类型比较。如果类型一致,字段值原样使用。如果 schema 表明该字段应为其他类型,Controller Service 会尝试把数据强制转换为 schema 指定的类型。如果无法强制转换,会抛出异常。
尝试把一个字段值从一种数据类型强制转换为另一种时,适用以下规则:
- 任何数据类型都可以强制转换为 String 类型。
- 任何数值类型(Byte、Short、Int、Long、Float、Double)都可以强制转换为其他数值类型。
- 任何数值都可以强制转换为 Date、Time 或 Timestamp 类型,方式是把 Long 值 视为自纪元(1970 年 1 月 1 日午夜,GMT)以来的毫秒数。
- 如果 String 值的格式与配置的 "Date Format"、"Time Format" 或 "Timestamp Format" 匹配,就可以强制转换为 Date、Time 或 Timestamp 类型。
- 如果值的类型合适,String 值可以强制转换为数值。例如,String 值
8可以强制转换为任何数值类型;但 String 值8.2只能强制转换为 Double 或 Float 类型,不能转为 Integer。 - String 值 "true" 或 "false"(不区分大小写)可以强制转换为 Boolean 值。
- 非空的 String 值可以强制转换为 Char 类型。如果字符串包含多个字符, 只使用第一个字符,其余忽略。
- 任何"日期/时间"类型(Date、Time、Timestamp)都可以强制转换为其他"日期/时间"类型。
- 任何"日期/时间"类型都可以强制转换为 Long 类型,表示自纪元(1970 年 1 月 1 日午夜,GMT)以来的毫秒数。
- 任何"日期/时间"类型都可以强制转换为 String。String 的格式就是对应属性 (Date Format、Time Format、Timestamp Format)所配置的 DateFormat。 如果未指定格式,值会被转换为自纪元(1970 年 1 月 1 日午夜,GMT)以来 毫秒数的字符串表示。
如果尝试在两种数据类型之间强制转换时上述规则都不适用,转换会失败并抛出异常。
Schema 推断
虽然 Lynxus 的 Record API 要求每条记录都有 schema,但基于数据中的值来推断 schema 往往比手动创建更方便。做法是把 "Schema Access Strategy" 属性设为 "Infer Schema"。使用该策略时,Reader 会先解析 FlowFile 中的所有数据, 记录遇到的所有字段及每个字段的类型。所有数据解析完成后,会形成一个涵盖 所有已遇到字段的 schema。
推断 schema 时的一个常见顾虑是如何处理两个值类型不同的情况。例如,考虑 包含以下两条记录的 FlowFile:
[
{
"name": "John",
"age": 8,
"values": "N/A"
},
{
"name": "Jane",
"age": "Ten",
"values": [
8,
"Ten"
]
}
]
显然 "name" 字段会被推断为 STRING 类型。但 "age" 字段该如何处理?应该是 INT 和 STRING 之间的 CHOICE 吗?应该优先用 LONG 而不是 INT 吗?直接用 STRING 吗?该字段应视为可为 null 吗?
为帮助理解该 Record Reader 如何推断 schema,以下是推断逻辑所遵循的规则:
- 所有字段都被推断为可为 null。
- 当两条不同记录中同一字段遇到两个值(或 ARRAY 类型遇到两个值)时, 推断引擎倾向于使用"更宽"的数据类型,而不是 CHOICE 类型。当且仅当数据 类型 "A" 除了包含 "B" 的所有值外还包含其他值时,才称 "A" 比 "B" 宽。 例如,LONG 类型比 INT 宽,但不比 BOOLEAN 宽(BOOLEAN 也不比 LONG 宽)。 INT 比 SHORT 宽。STRING 类型被认为比除 MAP、RECORD、ARRAY、CHOICE 之外的所有类型都宽。
- 当两条不同记录中同一字段遇到两个值(或 ARRAY 类型遇到两个值),
但两个值的类型互不比对方宽时,使用 CHOICE 类型。在上面的例子中,
"values" 字段会被推断为 STRING 或 ARRAY
之间的 CHOICE。 - 如果配置了 "Time Format"、"Timestamp Format" 或 "Date Format" 属性, 任何原本会被视为 STRING 类型的值,都会先与配置的格式比对,看是否匹配。 如果值匹配 Timestamp Format,视为 Timestamp 字段;匹配 Date Format 视为 Date 字段;匹配 Time Format 视为 Time 字段。如果值碰巧匹配多个 配置的格式,按 Timestamp、Date、Time 的顺序匹配。也就是说,如果一个值 同时匹配 Timestamp Format 和 Date Format,推断出的类型是 Timestamp。 由于解析日期和时间开销较大,如果不预期出现日期、时间和时间戳,或者可以 接受按 STRING 处理,建议不要配置这些格式。对于需要此功能的用例, 推断引擎足够智能,会先检查若干非常廉价的条件来优化解析。例如,先检查 字符串长度是否过长或过短、不可能匹配模式。这比尝试把每个字符串值都 解析为时间戳要高效得多。
- MAP 类型永远不会被推断,而是使用 RECORD 类型。
- 如果字段存在但所有值都是 null,该字段被推断为 STRING 类型。
推断 Schema 的缓存
该 Record Reader 要求:如果要推断 schema,必须读取所有记录,以确保推断出的 schema 适用于 FlowFile 中的所有记录。但这可能开销很大,尤其是数据要经过 很多不同转换时。为降低推断成本,可以通过设置同名属性为该 Record Reader 配置 "Schema Inference Cache"。这是一个可以在 Record Reader 和 Record Writer 之间共享的 Controller Service。
每当使用 Record Writer 写出数据时,如果它配置了 "Schema Cache",也会把 schema 添加到 Schema Cache 中。这会使该 schema 的标识符作为 Attribute 添加到 FlowFile 上。
每当使用 Record Reader 读取数据时,如果它配置了 "Schema Inference Cache", 会先在 FlowFile 上查找 "schema.cache.identifier" Attribute。如果该 Attribute 存在,就用它的值在 schema 缓存中查找 schema。如果能在缓存中 找到该标识符对应的 schema,就直接使用,而不必读取、解析和分析数据来 推断。如果 FlowFile 上没有该 Attribute,或缓存中没有对应标识符的 schema,Record Reader 会按上述方式推断。
最终效果是:用户可以串联许多不同的 Processor 来处理面向 Record 的数据。 通常只有链中的第一个 Processor 需要承担推断 schema 的"代价"。对链中的 其他所有 Processor,Record Reader 可以直接按标识符在 Schema Cache 中 查找。这使得 Record Reader 既能准确推断(基于 FlowFile 中的所有数据), 又能高效执行(无论多少 Processor 处理数据,通常只推断一次)。
起始字段策略(Starting Field Strategies)
使用 JsonTreeReader 时,可以选择两种不同的起始字段策略。使用默认的 Root Node 策略时,JsonTreeReader 从 JSON 的根元素开始处理,为整个 JSON 对象树创建 Record 对象;而 Nested Field 策略则指定一个嵌套字段 作为处理的起点。
使用 Nested Field 策略时,应指定与嵌套 JSON 部分对应的 schema。在 schema 推断的情况下,推断只针对嵌套部分执行。
示例
假设输入数据是以下 JSON:
[
{
"id": 17,
"name": "John",
"child": {
"id": "1"
},
"dob": "11-30-1983",
"siblingIds": [
4,
8
],
"siblings": [
{
"name": "Jeremy",
"id": 4
},
{
"name": "Julia",
"id": 8
}
]
},
{
"id": 98,
"name": "Jane",
"child": {
"id": 2
},
"dob": "08-30-1984",
"gender": "F",
"siblingIds": [],
"siblings": []
}
]
再假设为该 JSON 配置的 schema 如下(假设选择了 AvroSchemaRegistry Controller Service 来提供 Schema):
{
"namespace": "lynxus",
"name": "person",
"type": "record",
"fields": [
{
"name": "id",
"type": "int"
},
{
"name": "name",
"type": "string"
},
{
"name": "gender",
"type": "string"
},
{
"name": "dob",
"type": {
"type": "int",
"logicalType": "date"
}
},
{
"name": "siblings",
"type": {
"type": "array",
"items": {
"type": "record",
"name": "sibling",
"fields": [
{
"name": "name",
"type": "string"
}
]
}
}
}
]
}
再假设该 Controller Service 的 "Date Format" 属性配置为 "MM-dd-yyyy", 与 JSON 数据使用的日期格式一致。这会生成两条独立的记录,因为根元素是 包含两个元素的 JSON 数组。
第一条记录包含以下值:
| 字段名 | 字段值 |
|---|---|
| id | 17 |
| name | John |
| gender | null |
| dob | 11-30-1983 |
| siblings | 见下文 |
Siblings
包含两个元素的数组,每个元素本身是一条记录:
| 字段名 | 字段值 |
|---|---|
| name | Jeremy |
以及:
| 字段名 | 字段值 |
|---|---|
| name | Julia |
第二条记录包含以下值:
| 字段名 | 字段值 |
|---|---|
| id | 98 |
| name | Jane |
| gender | F |
| dob | 08-30-1984 |
| siblings | 空数组 |
Nested Field 策略
使用 Nested Field 策略,考虑同样的 JSON,指定的 Starting Field Name 为 "siblings"。为该 JSON 配置的 schema 如下:
{
"namespace": "lynxus",
"name": "siblings",
"type": "record",
"fields": [
{
"name": "name",
"type": "string"
},
{
"name": "id",
"type": "int"
}
]
}
第一条记录包含以下值:
| 字段名 | 字段值 |
|---|---|
| name | Jeremy |
| id | 4 |
第二条记录包含以下值:
| 字段名 | 字段值 |
|---|---|
| name | Julia |
| id | 8 |
Schema 应用策略(Schema Application Strategies)
当 JsonTreeReader 与 "Nested Field Strategy" 一起使用,且 "Schema Access Strategy" 不是 "Infer Schema" 时,可以把 schema 配置为应用于整个原始 JSON("Whole document" 策略),或应用于嵌套字段部分("Selected part" 策略)。