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);
}
};
}
}