57.2. 注解
创建的注解允许将模型的不同概念映射到 POJO,如下所示:
- 记录类型(CSV、键值对(如 FIX 消息)、固定 length …)
- 链接(指向另一个对象中的链接对象),
- DataField 及其属性(int、type、…)
- KeyValuePairField (用于 key = value 格式,如我们在修复财务消息中一样)
- 部分(用于识别标头、正文和页脚部分),
- OneToMany,
- BindyConverter,
- FormatFactories
本节将描述它们。
57.2.1. 1.CsvRecord 复制链接链接已复制到粘贴板!
CsvRecord 注解用于识别模型的根类。它代表记录 = " CSV 文件行",并可链接到多个子模型类。
| 注解名称 | 记录类型 | 级别 |
|---|---|---|
| CsvRecord | CSV | 类 |
| 参数名称 | 类型 | 必填 | 默认值 | info |
|---|---|---|---|---|
| 分隔符 | 字符串 | ✓ | 用于在令牌中分割记录的分隔符(必需)- 可以是 ',' 或 ';' 或 'anything'。支持的唯一空格字符是 tab (\t)。不支持其他空格字符(空格)。这个值被解释为正则表达式。如果要使用在正则表达式中具有特殊含义的符号,如 '|' 符号,您必须屏蔽它,如 '|'。 | |
| allowEmptyStream | 布尔值 | false | allowEmptyStream 参数将允许 prcoess CSV 文件无法查询的流。 | |
| autospanLine | 布尔值 | false | 最后一个记录涵盖其余行(可选)- 如果启用,则最后一列会自动跨越行尾,例如,如果其注释,则允许行包含所有字符,也是分隔符字符。 | |
| CRLF | 字符串 | WINDOWS | 用于在每次记录后(可选)添加回车符的字符 - 允许定义要使用的回车符。如果您指定之前列出的三个值,则您输入的值(custom)将用作 CRLF 字符。可以使用三个值:WINDOWS、UNIX、MAC 或自定义。 | |
| endWithLineBreak | 布尔值 | true | 如果 CSV 文件应该以换行符或没有行结尾(可选) | |
| generateHeaderColumns | 布尔值 | false | generateHeaderColumns 参数允许在 CSV 中添加,生成的标头包含列的名称 | |
| isOrdered | 布尔值 | false | 指明信息是否必须在输出中排序 | |
| name | 字符串 | 描述记录的名称(可选) | ||
| quote | 字符串 | " | 是否使用给定引号字符(可选)替换列(可选)- 在生成 CSV 时可以指定字段的引号字符。此注解与模型的根类关联,必须一次性声明。 | |
| 引用 | 布尔值 | false | 指明值(和标头)是否在 marshaling 时加上引号(可选) | |
| quotingEscaped | 布尔值 | false | 指明值在 quoting 时是否必须转义(可选) | |
| removeQuotes | 布尔值 | true | 如果 unmarshalling 应该为每个字段删除引号,则 remove quotes 参数标志 | |
| skipField | 布尔值 | false | skipField 参数将允许跳过 CSV 文件的字段。如果不需要一些字段,可以跳过它们。 | |
| skipFirstLine | 布尔值 | false | skipFirstLine 参数将允许跳过或不跳过 CSV 文件的第一行。此行通常包含列定义 |
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 {
}
请注意,如果要从对象到 CSV 并使用引号,则需要使用 @CsvRecord 上的 quote 属性来指定要使用的引号字符,如下所示:
@CsvRecord( separator = ",", quote = "\"" )
public Class Order {
}
case 5 : 分隔符和跳过FirstLine
当客户端要在文件的第一行(数据字段的名称)中时,这个功能非常有趣:
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 过程中绑定会生成 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
问题单 7:回车符
如果运行 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;
}
| __ |
|
57.2.2. 2.Link 复制链接链接已复制到粘贴板!
link 注解将允许将对象链接到一起。
| 注解名称 | 记录类型 | 级别 |
|---|---|---|
| Link | all | 类和属性 |
| 参数名称 | 类型 | 必填 | 默认值 | info |
|---|---|---|---|---|
| linkType | LinkType | OneToOne | 标识类间关系的链接类型 |
从当前版本中,只允许一对一的关系。
例如:如果模型类客户端链接到 Order 类,则使用 Order 类中的注解 Link,如下所示:
属性链接
@CsvRecord(separator = ",")
public class Order {
@DataField(pos = 1)
private int orderNr;
@Link
private Client client;
}
对于类客户端:
类链接
@Link
public class Client {
}
57.2.3. 3.DataField 复制链接链接已复制到粘贴板!
DataField 注解定义字段的属性。每个 datafield 都由记录中的位置、类型(字符串、int、date、…)以及可选的模式来标识。
| 注解名称 | 记录类型 | 级别 |
|---|---|---|
| DataField | all | 属性 |
| 参数名称 | 类型 | 必填 | 默认值 | info |
|---|---|---|---|---|
| pos | int | ✓ | 输入记录中数据的位置,必须从 1 开始(必需)。查看 position 参数。 | |
| align | 字符串 | R | 将文本与右侧或左对齐。使用值 <tt>R</tt> 或 <tt>L</tt>。 | |
| Clip | 布尔值 | false | 如果在使用固定长度时超过允许的长度,则指示在字段中允许的数据。 | |
| columnName | 字符串 |
标头列的名称(可选)。将属性的名称用作默认值。仅在 | ||
| decimalSeparator | 字符串 | 用于 BigDecimal 数字的十进制 Separator | ||
| defaultValue | 字符串 | 如果没有设置值,则字段的默认值 | ||
| delimiter | 字符串 | 如果字段有变量长度,则使用可选分隔符 | ||
| groupingSeparator | 字符串 | 当我们将 Separator 分组到要与 BigDecimal 号一起使用的 Separator,使用分组,例如 123,456.789 | ||
| impliedDecimalSeparator | 布尔值 | false | 指明是否有十进制点代表在指定位置 | |
| length | int | 0 | 如果记录设置为固定长度,则数据块的长度(字符数) | |
| lengthPos | int | 0 | 在记录中标识定义此字段预期的固定长度的 data 字段 | |
| 方法 | 字符串 | 在 DataField 上调用以应用此类自定义的方法名称。这必须是 datafield 本身的方法,或者您必须提供类方法的静态完全限定名称,如:请参阅单元测试 org.apache.camel.dataformat.bindy.csv.BindySimpleCsvFunctionWithExternalMethodTest.replaceToBar | ||
| name | 字符串 | 字段的名称(可选) | ||
| paddingChar | char | 如果记录被设置为固定长度,则带有 char to pad | ||
| pattern | 字符串 | Java 格式化器(例如 simpleDateFormat)的模式,它将用于转换数据(可选)。如果使用模式,则建议在绑定数据格式中设置 locale。设置为已知区域设置,如 "us" 或 "default" 来使用平台默认区域设置。 | ||
| position | int | 0 | 生成输出消息中的字段位置(应该从 1 开始)。在 CSV 生成的字段的位置(输出消息)的位置必须与输入位置(pos)不同时,必须使用。请参阅 pos 参数。 | |
| 精度 | int | 0 | 要创建的 \{@link java.math.BigDecimal} 号 | |
| required | 布尔值 | false | 指明字段是否强制 | |
| rounding | 字符串 | CEILING | 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: 123456.79. | |
| timezone | 字符串 | 要使用的时区。 | ||
| trim | 布尔值 | false | 指明值是否应该修剪 |
问题单 1:pos
此参数/attribute 代表 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;
}
问题单 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;
}
案例 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 属性将通知如何将字段放在 CSV 记录中。默认情况下,所用的位置与通过属性 pos 定义的位置对应。如果位置不同(这意味着我们有一个身份统计进程与 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 的 attribute isOrdered = true 结合使用。
问题单 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 :
问题单 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;
}
此属性仅适用于可选字段。
57.2.4. 4.FixedLengthRecord 复制链接链接已复制到粘贴板!
FixedLengthRecord 注解用于识别模型的根类。它代表记录 = "一个文件/消息行包含数据固定长度(字符数)格式",并可链接到多个子模型类。这个格式是有点的,因为字段的数据可以与右侧或左侧一致。
当数据的大小没有完全填满字段的长度时,我们可以添加"平板"字符。
| 注解名称 | 记录类型 | 级别 |
|---|---|---|
| FixedLengthRecord | 已修复 | 类 |
| 参数名称 | 类型 | 必填 | 默认值 | info |
|---|---|---|---|---|
| countGrapheme | 布尔值 | false | 指明如何计算收费 | |
| CRLF | 字符串 | WINDOWS | 用于在每次记录后添加回车符(可选)。可能的值: WINDOWS、UNIX、MAC 或自定义。这个选项仅在 marshalling 期间使用,除非自定义 eol,否则 unmarshalling 使用系统默认 JDK 提供的行分隔符。 | |
| EOL | 字符串 | 用来在 unmarshalling 时处理在各个记录后考虑行结尾的字符(可选 - default: "",它有助于使用默认 JDK 提供的行分隔符),除非提供任何其他行分隔符,否则此选项仅在 unmarshalling 期间使用,其中 marshalling 使用系统默认行分隔符作为"WINDOWS",除非提供了任何其他值。 | ||
| footer | 类 | void | 表示此类型的记录后可以跟随文件末尾的单个页脚记录 | |
| header | 类 | void | 表示此类型的记录在文件的开头之间可以加上一个标头记录 | |
| ignoreMissingChars | 布尔值 | false | 指明是否忽略短行 | |
| ignoreTrailingChars | 布尔值 | false | 当解压缩 / 解析时,可以忽略超出最后一个映射文件的字符。此注解与模型的根类关联,必须一次性声明。 | |
| length | int | 0 | 记录的固定长度(字符数)。这意味着记录始终是带有 \{#paddingChar ()} 的较长的 padded。 | |
| name | 字符串 | 描述记录的名称(可选) | ||
| paddingChar | char | 收费到平板, | ||
| skipFooter | 布尔值 | false | 配置数据格式以跳过页脚记录的 / unmarshalling。在主记录上配置此参数(例如,而不是标头或页脚)。 | |
| skipHeader | 布尔值 | false | 配置数据格式以跳过标题记录的 / unmarshalling / 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:修复带有校准和 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;
}
问题单 3: Field padding
有时,为记录定义的默认 padding 不能应用于字段,因为我们有一个数字格式,我们想使用 '0' 而不是 ' '。在这种情况下,您可以在模型中使用 @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
fixed- separated
@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 值。
案例 5:修复长度的长度记录,记录定义的字段长度
有时,固定长度记录可能包含一个字段,用来定义同一记录中另一个字段的预期长度。在以下示例中,instrumentNumber 字段值的长度由记录中的 instrumentNumberLen 字段的值定义。
10A9Pauline^M^ISIN10XD12345678BUYShare000002500.45USD01-08-2009
fixed- separated
@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;
}
问题单 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:在解析固定长度记录时跳过内容
通常,与提供固定长度记录的系统集成,其中包括超过目标用例所需的信息。在这种情况下,可以跳过我们不需要的这些字段的声明和解析。为保证这一点,如果下一个声明的字段的 pos 值超过最后一次解析字段的光标位置,Bindy 将跳过到记录中的下一个映射字段。对感兴趣的字段使用绝对 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
}
57.2.5. 5.消息 复制链接链接已复制到粘贴板!
Message 注解用于识别模型的类,它们将包含键值对字段。这种格式主要在财务交换协议消息(FIX)中使用。但是,此注解可用于任何其他格式,其中数据由键标识。密钥对值可以通过分隔符相互分隔,可以是类似 tab delimitor (unicode 表示 : \u0009)或头头(unicode 表示 : \u0001)的特殊字符。
要处理 FIX 消息,模型必须包含链接到根消息类的标头和 Trailer 类,可以是 Order 类。这不是强制性的,但当您结合使用 camel-bindy 与 camel-fix 的组合时,这是基于快速Fix 项目的修复网关。
| 注解名称 | 记录类型 | 级别 |
|---|---|---|
| 消息 | 键值对 | 类 |
| 参数名称 | 类型 | 必填 | 默认值 | 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、…)(可选) |
问题单 1:分隔符 = '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(keyValuePairSeparator = "=", pairSeparator = "\u0001", type="FIX", version="4.1")
public class Order {
}
查看测试情况
选项卡等 ASCII 字符 … 无法在 WIKI 页面中显示。因此,请看 camel-bindy 的测试案例,以查看 FIX 信息是如何显示的(https://github.com/apache/camel/blob/main/components/camel-bindy/src/test/data/fix/fix.txt)和 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)。
57.2.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 消息中的 key/tag 的位置必须不同时使用 | |
| 精度 | 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;
}
问题单 2:输出中的不同位置
如果我们将放入 FIX 消息中的 tags/keys 根据预定义的顺序进行排序,则使用注释 @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;
}
57.2.7. 7.部分 复制链接链接已复制到粘贴板!
在修复固定长度记录的消息中,在信息 : header、body 和 部分表示中,通常有不同的部分。注释 @Section 的目的是通知模型的绑定表示标头(= 第 1 节)、正文(= 部分 2)和页脚(= 第 3 节)
此注释仅存在一个属性/参数。
| 注解名称 | 记录类型 | 级别 |
|---|---|---|
| 部分 | 修复 | 类 |
| 参数名称 | 类型 | 必填 | 默认值 | info |
|---|---|---|---|---|
| number | int | ✓ | 部分的数量 |
问题单 1:部分
标头部分的定义:
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;
}
57.2.8. 8.OneToMany 复制链接链接已复制到粘贴板!
注释 @OneToMany 的目的是允许使用 List<?> 字段来定义 POJO 类,或者从包含重复组的记录。
OneToMany
的限制要小心,很多绑定都不允许处理在几个层次结构级别上定义的重复请求。
在以下情况下,关系 oneToMany ONLY 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;
}
问题单 2:阅读包含标签/密钥组的消息
以下是要在我们模型中处理的消息:
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 会重复。
和代码:
读取包含 tags/keys 组的 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;
}
57.2.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();
}
}
57.2.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);
}
};
}
}