17.3. 注解
创建的注解允许将您的模型的不同概念映射到 POJO,如下所示:
- 记录类型(CSV、键值对(如 FIX 消息)、固定长度 …)
- 链接(链接另一个对象中的对象)
- DataField 及其属性(int, type, …),
- KeyValuePairField (用于 key = FIX 财务消息一样的值格式),
- 部分(要识别标头、正文和页脚部分),
- OneToMany,
- BindyConverter,
- FormatFactories
本节将描述它们。
17.3.1. 1.CsvRecord
CsvRecord 注解用于识别模型的根类。它表示记录 = "CSV 文件的行",并可链接到多个子模型类。
注解名称 | 记录类型 | 级别 |
---|---|---|
CsvRecord | CSV | 类 |
参数名称 | 类型 | 必填 | 默认值 | info |
---|---|---|---|---|
分隔符 | 字符串 | ✓ | 用于在令牌中分割记录的分隔符(必需)- 可以是 ',' 或 ';' 或 'anything'。唯一支持的空格字符是 tab (\t)。不支持其他空格字符(空格)。这个值被解释为正则表达式。如果要使用在正则表达式中有一个特殊含义,例如 '|' 符号,则必须屏蔽它,如 '|'。 | |
allowEmptyStream | 布尔值 | false | allowEmptyStream 参数将允许 prcoess 的 CSV 文件不可避免的流。 | |
autospanLine | 布尔值 | false | 最后一条记录跨越其余行(可选)- 如果启用,则最后一列会自动跨越到行尾,例如,如果其注释,则允许行包含所有字符,也是分隔符 char。 | |
crlf | 字符串 | WINDOWS | 要在每个记录后添加回车符(可选)- 允许使用回车字符。如果您指定了之前列出的三个值,则您输入的值(自定义)将用作 CRLF 字符。可以使用三个值:WINDOWS、UNIX、MAC 或 custom。 | |
endWithLineBreak | 布尔值 | true | 如果 CSV 文件以换行符或未结束(可选) | |
generateHeaderColumns | 布尔值 | false | generateHeaderColumns 参数允许在 CSV 中添加生成的包含列名称的标头 | |
isOrdered | 布尔值 | false | 指明消息是否在输出中排序 | |
name | 字符串 | 描述记录的名称(可选) | ||
quote | 字符串 | " | 是否使用给定的引号字符(可选)- 在生成 CSV 时指定字段的引号字符。此注释与模型的根类关联,必须声明一次。 | |
引用 | 布尔值 | false | 指明在 marshaling (可选)时是否必须用引号括起值(和标头) | |
quotingEscaped | 布尔值 | false | 指明在引用时是否必须转义值(可选) | |
removeQuotes | 布尔值 | true | 如果 unmarshalling 应该尝试删除每个字段的引号,则 remove quotes 参数标志 | |
skipField | 布尔值 | false | skipField 参数将允许跳过 CSV 文件的字段。如果某些字段不需要,可以跳过它们。 | |
skipFirstLine | 布尔值 | false | skipFirstLine 参数将允许跳过或不是 CSV 文件的第一行。此行通常包含列定义 | |
trimLine | 布尔值 | true | 在将行解析为数据字段之前,是否修剪每行(stand 和 end)。 |
case 1 : separator = ','
用于隔离 CSV 记录中字段的分隔符是 ,
:
10, J, Pauline, M, XD12345678, Fortis Dynamic 15/15, 2500, USD, 08-01-2009
@CsvRecord( separator = "," ) public Class Order { }
case 2 : separator = ';'
与前面的问题单进行比较,这里的分隔符为 ;
而不是 :
10; J; Pauline; M; XD12345678; Fortis Dynamic 15/15; 2500; USD; 08-01-2009
@CsvRecord( separator = ";" ) public Class Order { }
case 3 : separator = '|'
与前面的问题单进行比较,这里的分隔符为 |
而不是 ;
:
10| J| Pauline| M| XD12345678| Fortis Dynamic 15/15| 2500| USD| 08-01-2009
@CsvRecord( separator = "\\|" ) public Class Order { }
case 4 : separator = '\",\"'
适用于 Camel 2.8.2 或更早版本
当要解析 CSV 记录的字段包含 ,
或 也会用作分隔符时,我们应该找到另一个策略来告知 camel bindy 如何处理这个问题单。
要使用逗号定义包含数据的字段,您可以使用单引号或双引号作为分隔符(例如 : '10', 'Street 10, NY', 'USA' 或 "10", "Street 10, NY", "USA")。
在这种情况下,将通过 bindy 删除行的第一个和最后一个字符,它们是单引号或双引号。
"10","J","Pauline"," M","XD12345678","Fortis Dynamic 15,15","2500","USD","08-01-2009"
@CsvRecord( separator = "\",\"" ) public Class Order { }
bindy 会自动检测记录是否用单引号或双引号括起,并在从 CSV 到对象时自动删除这些引号。因此 ,不要在 分隔符中包含引号,但只需如下所示:
"10","J","Pauline"," M","XD12345678","Fortis Dynamic 15,15","2500","USD","08-01-2009"
@CsvRecord( separator = "," ) public Class Order { }
请注意,如果要从 Object 放入 CSV 并使用引号,则需要使用 @CsvRecord
上的 quote
属性来指定要使用的引号,如下所示:
@CsvRecord( separator = ",", quote = "\"" ) public Class Order { }
case 5 : separator 和 skipFirstLine
当客户端想在文件的第一行(数据字段的名称)中时,该功能很有趣:
order id, client id, first name, last name, isin code, instrument name, quantity, currency, date
要通知绑定,必须在解析过程中跳过此第一行,我们使用属性 :
@CsvRecord(separator = ",", skipFirstLine = true) public Class Order { }
case 6 : generateHeaderColumns
要在生成的 CSV 第一行中添加,必须在注解中将 generateHeaderColumns 属性设置为 true,如下所示:
@CsvRecord( generateHeaderColumns = true ) public Class Order { }
因此,在 unmarshaling 过程中 Bindy 将生成类似以下的 CSV:
order id, client id, first name, last name, isin code, instrument name, quantity, currency, date 10, J, Pauline, M, XD12345678, Fortis Dynamic 15/15, 2500, USD, 08-01-2009
case 7 : carriage 返回
如果 camel-bindy 将运行的平台不是 Windows,但 Macintosh 或 Unix,您可以更改类似如下的 crlf 属性。提供三个值:WINDOWS、UNIX 或 MAC
@CsvRecord(separator = ",", crlf="MAC") public Class Order { }
另外,如果出于某种原因您需要添加不同的行尾字符,您可以选择使用 crlf 参数指定它。在以下示例中,我们可以使用逗号结束行,后跟换行符:
@CsvRecord(separator = ",", crlf=",\n") public Class Order { }
case 8 : isOrdered
有时,在从模型创建 CSV 记录时遵循的顺序与解析过程中使用的顺序不同。然后,在这种情况下,我们可以使用属性 isOrdered = true
来表示这一点与 DataField 注解的属性 位置
相结合。
@CsvRecord(isOrdered = true) public Class Order { @DataField(pos = 1, position = 11) private int orderNr; @DataField(pos = 2, position = 10) private String clientNr; }
pos
用于解析文件流,而 位置
则用于生成 CSV。
17.3.2. 2.Link
链接注解将允许将对象链接在一起。
注解名称 | 记录类型 | 级别 |
---|---|---|
Link | all | 类和属性 |
参数名称 | 类型 | 必填 | 默认值 | info |
---|---|---|---|---|
linkType | LinkType | OneToOne | 标识类之间关系的链接类型 |
从当前版本起,只允许一对一关系。
例如:如果模型类客户端链接到 Order 类,则使用 Order 类中的注解链接,如下所示:
属性链接
@CsvRecord(separator = ",") public class Order { @DataField(pos = 1) private int orderNr; @Link private Client client; }
对于类客户端:
类链接
@Link public class Client { }
17.3.3. 3.DataField
DataField 注解定义字段的属性。每个 datafield 都由记录中的位置、类型(字符串、int、日期、…)以及可选的模式来标识。
注解名称 | 记录类型 | 级别 |
---|---|---|
DataField | all | 属性 |
参数名称 | 类型 | 必填 | 默认值 | info |
---|---|---|---|---|
pos | int | ✓ | 数据在输入记录中的位置必须从 1 开始(必需)。请参阅 position 参数。 | |
align | 字符串 | R | 将文本与右侧或左对齐。使用值 <tt>R</tt> 或 <tt>L</tt>。 | |
Clip | 布尔值 | false | 如果在使用固定长度时超过允许的长度,则指示字段中冲突数据。 | |
columnName | 字符串 |
标头列的名称(可选)。使用属性的名称作为默认值。仅在 | ||
decimalSeparator | 字符串 | 与 BigDecimal 号码一起使用的十进制 9 月器 | ||
defaultValue | 字符串 | 如果没有设置值,则字段的默认值 | ||
delimiter | 字符串 | 如果字段有变量长度,要使用的可选分隔符 | ||
groupingSeparator | 字符串 | 当我们想通过组 e.g. 123,456.789 格式化/稀疏的数字时,分组了要与 BigDecimal 号码一起使用的 9 个分组。 | ||
impliedDecimalSeparator | 布尔值 | false | 指明是否存在代表在指定位置的十进制点 | |
length | int | 0 | 如果记录被设置为固定长度,则数据块的长度(字符数) | |
lengthPos | int | 0 | 识别记录中定义此字段预期的固定长度的数据字段 | |
方法 | 字符串 | 调用的方法名称,以在 DataField 上应用此类自定义。这必须是 datafield 本身上的方法,或者您必须提供类方法的静态完全限定名称,例如:请参阅单元测试 org.apache.camel.dataformat.bindy.csv.BindySimpleCsvFunctionWithExternalMethodTest.replaceToBar | ||
name | 字符串 | 字段的名称(可选) | ||
paddingChar | char | 如果记录被设置为固定长度,则为 char to pad | ||
pattern | 字符串 | Java formatter (示例为SimpleDateFormat)的模式将用于转换数据(可选)。如果使用模式,则建议在 bindy 数据格式上设置区域设置。设置为已知区域设置,如 "us",或使用 "default" 来使用平台默认区域设置。 | ||
position | int | 0 | 生成输出消息中的字段位置(从 1 开始)。当 CSV 生成的字段的位置(输出消息)中的字段位置与输入位置(pos)不同时,必须使用。请参阅 pos 参数。 | |
精度 | int | 0 | 要创建的 \{@link java.math.BigDecimal} 数字的精度 | |
required | 布尔值 | false | 指明字段是否强制 | |
Rounding | 字符串 | CEILING | Round mode to round/scale a bigDecimal Values : UP, DOWN, CEILING, FLOOR, HALF_UP, HALF_DOWN,HALF_EVEN, UNNECESSARY e.g : Number = 123456.789, Precision = 2, Rounding = CEILING Result: 123.79. | |
timezone | 字符串 | 要使用的时区。 | ||
trim | 布尔值 | false | 指明值是否应修剪 |
case 1:pos
这个参数/属性代表 CSV 记录中字段的位置。
position
@CsvRecord(separator = ",") public class Order { @DataField(pos = 1) private int orderNr; @DataField(pos = 5) private String isinCode; }
如本例中所示,位置从 1
开始,但仍然在类 Order 中从 5
开始。2
到 4
的数字在类客户端中定义(请参见此处)。
在另一个模型类中继续位置
public class Client { @DataField(pos = 2) private String clientNr; @DataField(pos = 3) private String firstName; @DataField(pos = 4) private String lastName; }
case 2:模式
模式允许增强或验证您的数据格式
pattern
@CsvRecord(separator = ",") public class Order { @DataField(pos = 1) private int orderNr; @DataField(pos = 5) private String isinCode; @DataField(name = "Name", pos = 6) private String instrumentName; @DataField(pos = 7, precision = 2) private BigDecimal amount; @DataField(pos = 8) private String currency; // pattern used during parsing or when the date is created @DataField(pos = 9, pattern = "dd-MM-yyyy") private Date orderDate; }
case 3 : 精度
当您想定义数字的十进制部分时,精度很有用。
精度
@CsvRecord(separator = ",") public class Order { @DataField(pos = 1) private int orderNr; @Link private Client client; @DataField(pos = 5) private String isinCode; @DataField(name = "Name", pos = 6) private String instrumentName; @DataField(pos = 7, precision = 2) private BigDecimal amount; @DataField(pos = 8) private String currency; @DataField(pos = 9, pattern = "dd-MM-yyyy") private Date orderDate; }
问题单 4:输出中的位置不同
position 属性将通知 bindy 如何将字段放在生成的 CSV 记录中。默认情况下,使用的位置对应于通过属性 pos
定义的位置。如果位置不同(这意味着我们有一个 asymetric processus marshaling from unmarshaling),则我们可以使用 位置
来指示这一点。
下面是一个示例:
输出中的位置不同
@CsvRecord(separator = ",", isOrdered = true) public class Order { // Positions of the fields start from 1 and not from 0 @DataField(pos = 1, position = 11) private int orderNr; @DataField(pos = 2, position = 10) private String clientNr; @DataField(pos = 3, position = 9) private String firstName; @DataField(pos = 4, position = 8) private String lastName; @DataField(pos = 5, position = 7) private String instrumentCode; @DataField(pos = 6, position = 6) private String instrumentNumber; }
注释 @DataField
的此属性必须与注释 @CsvRecord
的属性 isOrdered = true
结合使用。
case 5 : 必需
如果需要字段,只需使用 所需的
属性设为 true。
必填
@CsvRecord(separator = ",") public class Order { @DataField(pos = 1) private int orderNr; @DataField(pos = 2, required = true) private String clientNr; @DataField(pos = 3, required = true) private String firstName; @DataField(pos = 4, required = true) private String lastName; }
如果记录中没有此字段,则解析器将使用以下信息来引发错误:
Some fields are missing (optional or mandatory), line :
case 6 : trim
如果字段具有前导和/或尾随空格,应在处理前删除,只需使用属性 修剪
设置为 true。
Trim
@CsvRecord(separator = ",") public class Order { @DataField(pos = 1, trim = true) private int orderNr; @DataField(pos = 2, trim = true) private Integer clientNr; @DataField(pos = 3, required = true) private String firstName; @DataField(pos = 4) private String lastName; }
case 7 : defaultValue
如果没有定义字段,则使用 defaultValue
属性指示的值。
默认值
@CsvRecord(separator = ",") public class Order { @DataField(pos = 1) private int orderNr; @DataField(pos = 2) private Integer clientNr; @DataField(pos = 3, required = true) private String firstName; @DataField(pos = 4, defaultValue = "Barin") private String lastName; }
case 8 : columnName
仅在 @CsvRecord
具有注解 generateHeaderColumns = true
时指定属性的列名称。
列名称
@CsvRecord(separator = ",", generateHeaderColumns = true) public class Order { @DataField(pos = 1) private int orderNr; @DataField(pos = 5, columnName = "ISIN") private String isinCode; @DataField(name = "Name", pos = 6) private String instrumentName; }
此属性仅适用于可选字段。
17.3.4. 4.FixedLengthRecord
FixedLengthRecord 注解用于识别模型的根类。它代表 record = "文件/message 的行包含数据固定长度(字符数)格式,并可链接到多个子模型类。这个格式是一个具体格式,因为字段的数据可以与右侧或左侧一致。
当数据的大小没有完全填写字段长度时,我们可以添加"本"字符。
注解名称 | 记录类型 | 级别 |
---|---|---|
FixedLengthRecord | 已修复 | 类 |
参数名称 | 类型 | 必填 | 默认值 | info |
---|---|---|---|---|
countGrapheme | 布尔值 | false | 指明如何计算收费 | |
crlf | 字符串 | WINDOWS | 要在每个记录后添加回车字符(可选)。可能的值有:WINDOWS、UNIX、MAC 或 custom。这个选项仅在 marshalling 期间使用,而 unmarshalling 使用系统默认 JDK 提供的行分隔符,除非自定义 eol。 | |
EOL | 字符串 | 字符用于在每个记录后考虑行尾(可选 - default: ""),它可以帮助默认 JDK 提供的行分隔符使用,除非提供了任何其他值。)此选项仅在 unmarshalling 期间使用,其中 marshall 使用系统默认提供的行分隔符作为 "WINDOWS"。 | ||
footer | 类 | void | 表示此类型的记录可以跟随文件末尾的单个页脚记录 | |
header | 类 | void | 表示此类型的记录前面可以加上文件开头的单个标头记录 | |
ignoreMissingChars | 布尔值 | false | 指明是否忽略太短行 | |
ignoreTrailingChars | 布尔值 | false | 表示在 unmarshalling / 解析时会忽略最后一个映射文件之外的字符。此注释与模型的根类关联,必须声明一次。 | |
length | int | 0 | 记录的固定长度(字符数)。这意味着,记录将始终是使用 \{#paddingChar ()}'s 持续添加的 | |
name | 字符串 | 描述记录的名称(可选) | ||
paddingChar | char | 使用 char to pad。 | ||
skipFooter | 布尔值 | false | 配置数据格式,以跳过页脚记录的 marshalling / unmarshalling。在主记录上配置此参数(例如,不是标头或页脚)。 | |
skipHeader | 布尔值 | false | 配置数据格式,以跳过标头记录的 marshalling / unmarshalling。在主记录上配置此参数(例如,不是标头或页脚)。 |
记录不能是标头/页脚和主要固定长度记录。
问题单 1:简单固定长度记录
这个简单示例演示了如何设计模型来解析/格式化固定消息
10A9PaulineMISINXD12345678BUYShare2500.45USD01-08-2009
fixed-simple
@FixedLengthRecord(length=54, paddingChar=' ') public static class Order { @DataField(pos = 1, length=2) private int orderNr; @DataField(pos = 3, length=2) private String clientNr; @DataField(pos = 5, length=7) private String firstName; @DataField(pos = 12, length=1, align="L") private String lastName; @DataField(pos = 13, length=4) private String instrumentCode; @DataField(pos = 17, length=10) private String instrumentNumber; @DataField(pos = 27, length=3) private String orderType; @DataField(pos = 30, length=5) private String instrumentType; @DataField(pos = 35, precision = 2, length=7) private BigDecimal amount; @DataField(pos = 42, length=3) private String currency; @DataField(pos = 45, length=10, pattern = "dd-MM-yyyy") private Date orderDate; }
问题单 2:使用 alignment 和 padding 修复长度记录
此更详细的示例演示了如何定义字段的对齐以及如何分配 padding 字符,该字符为 ' '
:
10A9 PaulineM ISINXD12345678BUYShare2500.45USD01-08-2009
fixed-padding-align
@FixedLengthRecord(length=60, paddingChar=' ') public static class Order { @DataField(pos = 1, length=2) private int orderNr; @DataField(pos = 3, length=2) private String clientNr; @DataField(pos = 5, length=9) private String firstName; @DataField(pos = 14, length=5, align="L") // align text to the LEFT zone of the block private String lastName; @DataField(pos = 19, length=4) private String instrumentCode; @DataField(pos = 23, length=10) private String instrumentNumber; @DataField(pos = 33, length=3) private String orderType; @DataField(pos = 36, length=5) private String instrumentType; @DataField(pos = 41, precision = 2, length=7) private BigDecimal amount; @DataField(pos = 48, length=3) private String currency; @DataField(pos = 51, length=10, pattern = "dd-MM-yyyy") private Date orderDate; }
case 3:字段填充
有时,为记录定义的默认 padding 不能应用于字段,因为我们有一个数字格式,其中我们希望使用 '0' 而不是 ' ' pad。在这种情况下,您可以在模型中使用 @DataField
上的属性 paddingChar
来设置这个值。
10A9 PaulineM ISINXD12345678BUYShare000002500.45USD01-08-2009
fixed-padding-field
@FixedLengthRecord(length = 65, paddingChar = ' ') public static class Order { @DataField(pos = 1, length = 2) private int orderNr; @DataField(pos = 3, length = 2) private String clientNr; @DataField(pos = 5, length = 9) private String firstName; @DataField(pos = 14, length = 5, align = "L") private String lastName; @DataField(pos = 19, length = 4) private String instrumentCode; @DataField(pos = 23, length = 10) private String instrumentNumber; @DataField(pos = 33, length = 3) private String orderType; @DataField(pos = 36, length = 5) private String instrumentType; @DataField(pos = 41, precision = 2, length = 12, paddingChar = '0') private BigDecimal amount; @DataField(pos = 53, length = 3) private String currency; @DataField(pos = 56, length = 10, pattern = "dd-MM-yyyy") private Date orderDate; }
问题单 4:带有分隔符的固定长度记录
修复了长度记录有时会在记录内有分隔的内容。firstName 和 lastName 字段使用以下示例中的 ^
字符分隔:
10A9Pauline^M^ISINXD12345678BUYShare000002500.45USD01-08-2009
固定分隔
@FixedLengthRecord public static class Order { @DataField(pos = 1, length = 2) private int orderNr; @DataField(pos = 2, length = 2) private String clientNr; @DataField(pos = 3, delimiter = "^") private String firstName; @DataField(pos = 4, delimiter = "^") private String lastName; @DataField(pos = 5, length = 4) private String instrumentCode; @DataField(pos = 6, length = 10) private String instrumentNumber; @DataField(pos = 7, length = 3) private String orderType; @DataField(pos = 8, length = 5) private String instrumentType; @DataField(pos = 9, precision = 2, length = 12, paddingChar = '0') private BigDecimal amount; @DataField(pos = 10, length = 3) private String currency; @DataField(pos = 11, length = 10, pattern = "dd-MM-yyyy") private Date orderDate; }
可以使用 ordinal, sequential 值而不是精确列号来定义固定长度记录中的 pos
值。
case 5 :带有记录定义字段长度的固定长度记录
有时,固定长度记录可能会包含一个字段,用于定义同一记录中另一个字段的预期长度。在以下示例中,instrumentNumber
字段值的长度由记录中的 instrumentNumberLen
字段的值定义。
10A9Pauline^M^ISIN10XD12345678BUYShare000002500.45USD01-08-2009
固定分隔
@FixedLengthRecord public static class Order { @DataField(pos = 1, length = 2) private int orderNr; @DataField(pos = 2, length = 2) private String clientNr; @DataField(pos = 3, delimiter = "^") private String firstName; @DataField(pos = 4, delimiter = "^") private String lastName; @DataField(pos = 5, length = 4) private String instrumentCode; @DataField(pos = 6, length = 2, align = "R", paddingChar = '0') private int instrumentNumberLen; @DataField(pos = 7, lengthPos=6) private String instrumentNumber; @DataField(pos = 8, length = 3) private String orderType; @DataField(pos = 9, length = 5) private String instrumentType; @DataField(pos = 10, precision = 2, length = 12, paddingChar = '0') private BigDecimal amount; @DataField(pos = 11, length = 3) private String currency; @DataField(pos = 12, length = 10, pattern = "dd-MM-yyyy") private Date orderDate; }
case 6:带有标头和页脚的固定长度记录
bindy 将发现作为模型的一部分配置的固定长度标头和页脚记录 - 只要注解的类存在于与主 @FixedLengthRecord
类相同的软件包中,或者在其中一个配置的扫描软件包中。以下文本说明了两个固定长度记录,它们被标头记录和页脚记录括起来。
101-08-2009 10A9 PaulineM ISINXD12345678BUYShare000002500.45USD01-08-2009 10A9 RichN ISINXD12345678BUYShare000002700.45USD01-08-2009 9000000002
fixed-header-and-footer-main-class
@FixedLengthRecord(header = OrderHeader.class, footer = OrderFooter.class) public class Order { @DataField(pos = 1, length = 2) private int orderNr; @DataField(pos = 2, length = 2) private String clientNr; @DataField(pos = 3, length = 9) private String firstName; @DataField(pos = 4, length = 5, align = "L") private String lastName; @DataField(pos = 5, length = 4) private String instrumentCode; @DataField(pos = 6, length = 10) private String instrumentNumber; @DataField(pos = 7, length = 3) private String orderType; @DataField(pos = 8, length = 5) private String instrumentType; @DataField(pos = 9, precision = 2, length = 12, paddingChar = '0') private BigDecimal amount; @DataField(pos = 10, length = 3) private String currency; @DataField(pos = 11, length = 10, pattern = "dd-MM-yyyy") private Date orderDate; } @FixedLengthRecord public class OrderHeader { @DataField(pos = 1, length = 1) private int recordType = 1; @DataField(pos = 2, length = 10, pattern = "dd-MM-yyyy") private Date recordDate; } @FixedLengthRecord public class OrderFooter { @DataField(pos = 1, length = 1) private int recordType = 9; @DataField(pos = 2, length = 9, align = "R", paddingChar = '0') private int numberOfRecordsInTheFile; }
case 7 : 在解析固定长度记录时跳过内容
通常与提供固定长度记录的系统集成,其包含比目标用例所需信息更多的系统。在这种情况下,跳过我们不需要的字段的声明和解析。要接受这一点,Bindy 将跳过记录中的下一个映射字段,如果下一个声明字段的 pos
值不在最后一个解析字段的光标位置。对感兴趣的字段使用绝对 pos
位置(而不是 ordinal 值)会导致 Bindy 在两个字段之间跳过内容。
同样,可能不关注某些字段之外的内容。在这种情况下,您可以通过设置 @FixedLengthRecord
声明上的 ignoreTrailingChars
属性来告知 Bindy 跳过除最后一个映射字段以外的所有内容解析。
@FixedLengthRecord(ignoreTrailingChars = true) public static class Order { @DataField(pos = 1, length = 2) private int orderNr; @DataField(pos = 3, length = 2) private String clientNr; // any characters that appear beyond the last mapped field will be ignored }
17.3.5. 5.消息
Message 注解用于识别包含键值对字段的模型类。此类格式主要在 financial Exchange Protocol Messages (FIX)中使用。但是,此注解可用于通过密钥标识数据的任何其他格式。密钥对值由分隔符相互分隔,可以是诸如 tab delimitor (unicode representation : \u0009
)或标题的开头(unicode representation : \u0001
)
要使用 FIX 消息,模型必须包含链接到根消息类的标头和 Trailer 类,可以是 Order 类。这不是强制要求,当您将 camel-bindy 与 camel-fix 结合使用时,这是基于 quickFix 项目的修复网关时,将非常有用。
注解名称 | 记录类型 | 级别 |
---|---|---|
消息 | 键值对 | 类 |
参数名称 | 类型 | 必填 | 默认值 | info |
---|---|---|---|---|
keyValuePairSeparator | 字符串 | ✓ | 键值对分隔符用于从其键中分离值(必需)。可以是 '\u0001', '\u0009', '#' 或 'anything'。 | |
pairSeparator | 字符串 | ✓ | 用于在令牌中分割键值对的对(必需)。可以是 '=', ';', 或 'anything'。 | |
crlf | 字符串 | WINDOWS | 要在每个记录后添加回车字符(可选)。可能的值 = WINDOWS、UNIX、MAC 或 custom。如果您指定了之前列出的三个值,则您输入的值(自定义)将用作 CRLF 字符。 | |
isOrdered | 布尔值 | false | 指明消息是否在输出中排序。此注释与模型的消息类关联,必须声明一次。 | |
name | 字符串 | 描述消息的名称(可选) | ||
type | 字符串 | 修复 | type 用于定义消息的类型(如 FIX、EMX、…)(可选) | |
version | 字符串 | 4.1 | version 定义消息的版本(如 4.1、…)(可选) |
case 1 : separator = 'u0001'
FIX 消息中用于隔离键值对字段的分隔符是 ASCII 01
字符或 unicode 格式 \u0001
。此字符必须第二次转义,以避免 java 运行时错误。下面是一个示例:
8=FIX.4.1 9=20 34=1 35=0 49=INVMGR 56=BRKR 1=BE.CHM.001 11=CHM0001-01 22=4 ...
以及如何使用注解:
FIX - message
@Message(keyValuePairSeparator = "=", pairSeparator = "\u0001", type="FIX", version="4.1") public class Order { }
查看测试问题单
ASCII 字符,如 tab,… 无法显示在 WIKI 页面中。因此,请看一个 camel-bindy 测试案例,来准确查看 FIX 消息是什么样子和 Order,Trailer, Header class (https://github.com/apache/camel/blob/main/components/camel-bindy/src/test/java/org/apache/camel/dataformat/bindy/model/fix/simple/Order.java)。
17.3.6. 6.KeyValuePairField
KeyValuePairField 注解定义键值对字段的属性。每个 KeyValuePairField 都由一个标签(= key)及其关联的值标识,一个类型(字符串、int、date、…)、可选模式以及是否需要字段。
注解名称 | 记录类型 | 级别 |
---|---|---|
KeyValuePairField | Key Value Pair - FIX | 属性 |
参数名称 | 类型 | 必填 | 默认值 | info |
---|---|---|---|---|
tag | int | ✓ | 标识消息中的字段的标签(必需)- 必须是唯一的 | |
impliedDecimalSeparator | 布尔值 | false | <b>Camel 2.11:</b> 表示在指定位置是否有十进制点 | |
name | 字符串 | 字段的名称(可选) | ||
pattern | 字符串 | 格式者将用于转换数据的模式(可选) | ||
position | int | 0 | 字段在生成的消息中的位置 - 当 FIX 消息中的键/标签的位置必须不同时,必须使用 | |
精度 | int | 0 | 要创建的 BigDecimal 数量的精度 | |
required | 布尔值 | false | 指明字段是否强制 | |
timezone | 字符串 | 要使用的时区。 |
问题单 1:标签
这个参数代表消息中字段的密钥:
FIX message - Tag
@Message(keyValuePairSeparator = "=", pairSeparator = "\u0001", type="FIX", version="4.1") public class Order { @Link Header header; @Link Trailer trailer; @KeyValuePairField(tag = 1) // Client reference private String Account; @KeyValuePairField(tag = 11) // Order reference private String ClOrdId; @KeyValuePairField(tag = 22) // Fund ID type (Sedol, ISIN, ...) private String IDSource; @KeyValuePairField(tag = 48) // Fund code private String SecurityId; @KeyValuePairField(tag = 54) // Movement type ( 1 = Buy, 2 = sell) private String Side; @KeyValuePairField(tag = 58) // Free text private String Text; }
case 2:输出中的不同位置
如果我们将放入 FIX 消息中的标签/密钥按照预定义的顺序排序,则使用注释 @KeyValuePairField
的属性 位置
。
FIX message - Tag - sort
@Message(keyValuePairSeparator = "=", pairSeparator = "\\u0001", type = "FIX", version = "4.1", isOrdered = true) public class Order { @Link Header header; @Link Trailer trailer; @KeyValuePairField(tag = 1, position = 1) // Client reference private String account; @KeyValuePairField(tag = 11, position = 3) // Order reference private String clOrdId; }
17.3.7. 7.部分
在固定长度记录的 FIX 消息中,通常以信息 : header, body 和 section 表示不同的部分。注释的 @Section
的目的是告知绑定模型中哪个类代表标头(= section 1)、body (= section 2)和页脚(= section 3)
此注释仅存在一个属性/参数。
注解名称 | 记录类型 | 级别 |
---|---|---|
部分 | 修复 | 类 |
参数名称 | 类型 | 必填 | 默认值 | info |
---|---|---|---|---|
number | int | ✓ | 部分数 |
问题单 1:部分
header 部分的定义:
FIX message - Section - Header
@Section(number = 1) public class Header { @KeyValuePairField(tag = 8, position = 1) // Message Header private String beginString; @KeyValuePairField(tag = 9, position = 2) // Checksum private int bodyLength; }
body 部分的定义:
FIX message - Section - Body
@Section(number = 2) @Message(keyValuePairSeparator = "=", pairSeparator = "\\u0001", type = "FIX", version = "4.1", isOrdered = true) public class Order { @Link Header header; @Link Trailer trailer; @KeyValuePairField(tag = 1, position = 1) // Client reference private String account; @KeyValuePairField(tag = 11, position = 3) // Order reference private String clOrdId;
footer 部分的定义:
FIX message - Section - Footer
@Section(number = 3) public class Trailer { @KeyValuePairField(tag = 10, position = 1) // CheckSum private int checkSum; public int getCheckSum() { return checkSum; }
17.3.8. 8.OneToMany
注释 @OneToMany
的目的是允许处理 List<?&
gt; 字段定义的 POJO 类或包含重复组的记录。
OneToMany
的限制要小心,很多绑定都不允许处理在多个层次上定义的重复。
在以下情况下,关系仅是 WORKS:
- 读取包含重复组的 FIX 消息(= 标签/密钥组)
- 生成带有重复数据的 CSV
注解名称 | 记录类型 | 级别 |
---|---|---|
OneToMany | all | 属性 |
参数名称 | 类型 | 必填 | 默认值 | info |
---|---|---|---|---|
mappedTo | 字符串 | 与 Class> 的 List<Type 类型关联的类名称 |
问题单 1:生成带有重复数据的 CSV
以下是我们需要的 CSV 输出:
Claus,Ibsen,Camel in Action 1,2010,35 Claus,Ibsen,Camel in Action 2,2012,35 Claus,Ibsen,Camel in Action 3,2013,35 Claus,Ibsen,Camel in Action 4,2014,35
重复数据涉及书及其发布日期的标题,第一个、姓氏和年龄很常见,以及用于建模的类。Author 类包含 Book 的列表。
使用重复数据生成 CSV
@CsvRecord(separator=",") public class Author { @DataField(pos = 1) private String firstName; @DataField(pos = 2) private String lastName; @OneToMany private List<Book> books; @DataField(pos = 5) private String Age; } public class Book { @DataField(pos = 3) private String title; @DataField(pos = 4) private String year; }
case 2 : Reading FIX message containing group of tags/keys
以下是我们希望在我们的模型中处理的消息:
8=FIX 4.19=2034=135=049=INVMGR56=BRKR 1=BE.CHM.00111=CHM0001-0158=this is a camel - bindy test 22=448=BE000124567854=1 22=548=BE000987654354=2 22=648=BE000999999954=3 10=220
标签 22、48 和 54 重复。
和代码:
读取包含标签/密钥组的 FIX 消息
public class Order { @Link Header header; @Link Trailer trailer; @KeyValuePairField(tag = 1) // Client reference private String account; @KeyValuePairField(tag = 11) // Order reference private String clOrdId; @KeyValuePairField(tag = 58) // Free text private String text; @OneToMany(mappedTo = "org.apache.camel.dataformat.bindy.model.fix.complex.onetomany.Security") List<Security> securities; } public class Security { @KeyValuePairField(tag = 22) // Fund ID type (Sedol, ISIN, ...) private String idSource; @KeyValuePairField(tag = 48) // Fund code private String securityCode; @KeyValuePairField(tag = 54) // Movement type ( 1 = Buy, 2 = sell) private String side; }
17.3.9. 9.BindyConverter
注释的 @BindyConverter
定义了在字段级别上使用的转换器。提供的类必须实施 Format 接口。
@FixedLengthRecord(length = 10, paddingChar = ' ') public static class DataModel { @DataField(pos = 1, length = 10, trim = true) @BindyConverter(CustomConverter.class) public String field1; } public static class CustomConverter implements Format<String> { @Override public String format(String object) throws Exception { return (new StringBuilder(object)).reverse().toString(); } @Override public String parse(String string) throws Exception { return (new StringBuilder(string)).reverse().toString(); } }
17.3.10. 10.FormatFactories
注释的 @FormatFactories
的目的是在记录级别定义一组转换器。提供的类必须实施 FormatFactoryInterface
接口。
@CsvRecord(separator = ",") @FormatFactories({OrderNumberFormatFactory.class}) public static class Order { @DataField(pos = 1) private OrderNumber orderNr; @DataField(pos = 2) private String firstName; } public static class OrderNumber { private int orderNr; public static OrderNumber ofString(String orderNumber) { OrderNumber result = new OrderNumber(); result.orderNr = Integer.valueOf(orderNumber); return result; } } public static class OrderNumberFormatFactory extends AbstractFormatFactory { { supportedClasses.add(OrderNumber.class); } @Override public Format<?> build(FormattingOptions formattingOptions) { return new Format<OrderNumber>() { @Override public String format(OrderNumber object) throws Exception { return String.valueOf(object.orderNr); } @Override public OrderNumber parse(String string) throws Exception { return OrderNumber.ofString(string); } }; } }