20.3. 注解
创建的注解允许将不同的模型概念映射到 POJO,如下所示:
- 记录类型(CSV,键值对(如 FIX 消息)、固定长度 …),
- 链接(到另一个对象中的链接对象),
- DataField 及其属性(int, type, …),
- KeyValuePairField (用于 key = 值格式,如我们在 FIX 财务消息中一样),
- 部分(识别标题、正文和页脚部分)、
- OneToMany,
- BindyConverter,
- FormatFactories
本节将描述它们。
20.3.1. 1.CsvRecord 复制链接链接已复制到粘贴板!
CsvRecord 注解用于标识模型的根类。它代表一个 record = "一个 CSV 文件的行",并可链接到几个子模型类。
| 注解名称 | 记录类型 | 级别 |
|---|---|---|
| CsvRecord | CSV | 类 |
| 参数名称 | 类型 | 必填 | 默认值 | info |
|---|---|---|---|---|
| 分隔符 | 字符串 | ✓ | 用于分割令牌中的记录的分隔符(必需)- 可以是 '、' 或 ';' 或 'anything'。唯一支持空格字符是 tab (\t)。不支持其他空格字符(空格)。这个值被解释为正则表达式。如果要使用在正则表达式中有一个特殊含义的符号,例如 '|' 符号,您必须屏蔽它,如 '|'。 | |
| allowEmptyStream | 布尔值 | false | allowEmptyStream 参数允许 prcoess 的 unavaiable 流。 | |
| autospanLine | 布尔值 | false | 最后一个记录跨越其余行(可选)- 如果启用,则最后一列将自动生成到行尾,例如,如果是注释,则这允许行包含所有字符,也是分隔符字符。 | |
| CRLF | 字符串 | WINDOWS | 用于在每条记录后添加回车符(可选)- 允许定义要使用的回车符。如果您指定了前面列出的三个值,则您输入的值(custom)将用作 CRLF 字符。可以使用三个值:WINDOWS、UNIX、MAC 或自定义。 | |
| endWithLineBreak | 布尔值 | true | 如果 CSV 文件应当以换行符或未结束(可选)结尾,则 endWithLineBreak 参数标志。 | |
| generateHeaderColumns | 布尔值 | false | generateHeaderColumns 参数允许在 CSV 中添加,生成包含列名称的标头 | |
| isOrdered | 布尔值 | false | 指明消息是否在输出中排序 | |
| name | 字符串 | 描述记录的名称(可选) | ||
| quote | 字符串 | " | 是否使用给定引号字符(可选)进行 marshal 列- 允许在生成 CSV 时指定字段的引号字符。此注解与模型的根类关联,必须被声明一次。 | |
| quoting | 布尔值 | false | 指明在 marshaling (可选)时是否必须加引号值(和标头) | |
| quotingEscaped | 布尔值 | false | 指明在 quoting (可选)时是否必须转义值。 | |
| 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, NYA")。
在这种情况下,行的第一个和最后一个字符是单引号或双引号将被绑定删除。
"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 : 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 过程中绑定将生成 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 return
如果运行 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。
20.3.2. 2.Link 复制链接链接已复制到粘贴板!
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 {
}
20.3.3. 3.DataField 复制链接链接已复制到粘贴板!
DataField 注解定义字段的属性。每个 datafield 都由其在记录中的位置、类型(字符串、int、date、…)以及可选的模式来标识。
| 注解名称 | 记录类型 | 级别 |
|---|---|---|
| DataField | all | 属性 |
| 参数名称 | 类型 | 必填 | 默认值 | info |
|---|---|---|---|---|
| pos | int | ✓ | 数据在输入记录中的位置必须从 1 开始(必需)。请参阅 position 参数。 | |
| 对齐 | 字符串 | R | 将文本对齐到右或左侧。使用值 <tt>R</tt> 或 <tt>L</tt>。 | |
| Clip | 布尔值 | false | 如果在使用固定长度时超过允许的长度,则指示 字段中 clip 数据。 | |
| columnName | 字符串 |
标头列的名称(可选)。将属性的名称用作默认值。仅在 | ||
| decimalSeparator | 字符串 | 用于 BigDecimal 数字的十进制 Separator | ||
| defaultValue | 字符串 | 如果没有设置值,则字段的默认值 | ||
| delimiter | 字符串 | 如果字段有变量长度,则使用可选的分隔符 | ||
| groupingSeparator | 字符串 | 当我们想通过分组(如 123,456.789)对数字进行格式化/解析时,对 Separator 的 groupinging Separator 一同使用 | ||
| impliedDecimalSeparator | 布尔值 | false | 表示在指定位置上是否有十进制点表示 | |
| length | int | 0 | 如果记录设置为固定长度,则数据块的长度(字符数) | |
| lengthPos | int | 0 | 标识记录中的 data 字段,该字段定义此字段的预期固定长度 | |
| 方法 | 字符串 | 调用在 DataField 上应用此类自定义的方法名称。这必须是 datafield 本身的方法,或者您必须提供类方法的静态完全限定名称,例如:查看单元 test org.apache.camel.dataformat.bindy.csv.BindySimpleCsvFunctionWithExternalMethodTest.replaceToBar | ||
| name | 字符串 | 字段名称(可选) | ||
| paddingChar | char | 如果记录被设置为固定长度,使用 char to pad | ||
| pattern | 字符串 | Java 格式器(例如,SimpleDateFormat)的模式将用于转换数据(可选)。如果使用模式,则建议在绑定数据格式上设置区域设置。设置为一个已知的区域设置,如 "us",或使用 "default" 来使用平台默认区域设置。 | ||
| position | int | 0 | 生成输出消息中的字段位置(应该从 1 开始)。当 CSV 中字段的位置(输出消息)必须与输入位置不同时,必须使用。请参阅 pos 参数。 | |
| 精度 | int | 0 | 要创建的 \{@link java.math.BigDecimal} 号的精度 | |
| required | 布尔值 | false | 指明字段是否是必须的 | |
| 舍入 | 字符串 | 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.456 | |
| timezone | 字符串 | 要使用的时区。 | ||
| trim | 布尔值 | false | 指明该值是否应修剪 |
问题单 1:pos
此参数/属性表示 CSV 记录中字段的位置。
position
@CsvRecord(separator = ",")
public class Order {
@DataField(pos = 1)
private int orderNr;
@DataField(pos = 5)
private String isinCode;
}
如本例中所示,位置从 1 开始,但在类顺序中继续为 5。类客户端中定义了从 2 到 4 的数字(在此之后查看)。
position 继续在另一个模型类中
public class Client {
@DataField(pos = 2)
private String clientNr;
@DataField(pos = 3)
private String firstName;
@DataField(pos = 4)
private String lastName;
}
case 2 : pattern
该模式可以增强或验证您的数据格式
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 属性将告知如何将字段放在生成的 CSV 记录中。默认情况下,使用的位置对应于与属性 pos 定义的位置。如果位置不同(这意味着我们有一个 asymetric processus 比较来自 unmarshaling 的 marshaling),那么我们可以使用 位置 来指明这一点。
下面是一个示例:
位置在输出中有所不同
@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 结合使用。
case 5 : required
如果一个字段是必需的,只需使用需要设置为 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
如果一个字段在处理前有前导和/或尾随空格,则只需使用属性 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;
}
此属性仅适用于可选字段。
20.3.4. 4.FixedLengthRecord 复制链接链接已复制到粘贴板!
FixedLengthRecord 注解用于标识模型的根类。它代表一条记录 = "文件/消息的行包含数据固定长度(字符数)格式",并可链接到多个子级类。这个格式非常具体,因为字段的数据可以与右侧或左侧保持一致。
当数据的大小没有完全填写字段的长度时,我们可以添加"平板"字符。
| 注解名称 | 记录类型 | 级别 |
|---|---|---|
| FixedLengthRecord | 已修复 | 类 |
| 参数名称 | 类型 | 必填 | 默认值 | info |
|---|---|---|---|---|
| countGrapheme | 布尔值 | false | 指明如何计算字符 | |
| CRLF | 字符串 | WINDOWS | 用于在每个记录后添加回车的字符(可选)。可能的值有:WINDOWS、UNIX、MAC 或自定义。这个选项仅在 marshalling 期间使用,unmarshalling 使用系统默认 JDK 提供的行分隔符,除非自定义了 eol。 | |
| EOL | 字符串 | 在 unmarshalling 时,要在每个记录后处理行尾的字符(可选 - default: "" )可帮助默认的 JDK 提供的行分隔符,除非提供任何其他行分隔符。 | ||
| footer | 类 | void | 表示此类型的记录可能后跟一个文件末尾的一个页脚记录 | |
| header | 类 | void | 表示此类型的记录前面可能在文件的开头有一个标头记录 | |
| ignoreMissingChars | 布尔值 | false | 指明是否忽略了太短行 | |
| ignoreTrailingChars | 布尔值 | false | 表示当 unmarshalling / 解析时,可以忽略除最后一个映射的文件以外的字符。此注解与模型的根类关联,必须被声明一次。 | |
| length | int | 0 | 记录的固定长度(字符数)。这意味着记录始终会用 \{""paddingChar ()}'s 长期添加。 | |
| name | 字符串 | 描述记录的名称(可选) | ||
| paddingChar | char | 使用 pad 进行字符. | ||
| skipFooter | 布尔值 | false | 配置数据格式,以跳过页脚记录的 marshalling / unmarshalling。在主记录上配置此参数(例如,而不是标头或页脚)。 | |
| skipHeader | 布尔值 | false | 配置数据格式,以跳过标题记录的 / 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 字符,该字符是 ' 这里:
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' 而不是 ''。在这种情况下,您可以在模型中使用 @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-delimited
@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、顺序值而不是精确列号定义固定长度记录中的 pos 值。
问题单 5 :使用记录定义字段长度修复长度记录
有时,固定长度记录可能包含一个字段,用于定义同一记录中另一个字段的预期长度。在以下示例中,inst Number 字段值 的长度由记录中的 instrumentNumberLen 字段的值定义。
10A9Pauline^M^ISIN10XD12345678BUYShare000002500.45USD01-08-2009
fixed-delimited
@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
}
20.3.5. 5.消息 复制链接链接已复制到粘贴板!
Message 注解用于标识将包含键值对字段的模型类。这种格式主要在 Exchange Protocol 协议消息(FIX)中使用。不过,此注解可用于通过键标识数据的任何其他格式。密钥对值彼此分隔为分隔符,可以是诸如 tab delimitor (unicode representation : \u0009)或头开头(unicode 表示 : \u0001)的特殊字符。
要使用 FIX 消息,模型必须包含链接到根消息类的 Header 和 Trailer 类,该类可以是 Order 类。这并不是强制性的,但当您将 camel-bindy 与 camel-fix 结合使用时,这是基于 fastFix 项目修复网关时非常有用。
| 注解名称 | 记录类型 | 级别 |
|---|---|---|
| 消息 | 键值对 | 类 |
| 参数名称 | 类型 | 必填 | 默认值 | info |
|---|---|---|---|---|
| keyValuePairSeparator | 字符串 | ✓ | 键值对分隔符用于从其键(必需)中分割值。可以是 '\u0001', '\u0009', '#', 或 'anything'。 | |
| pairSeparator | 字符串 | ✓ | 用于在令牌中分割键值对(必需)的对。可以是 '=', ';', 或 'anything'。 | |
| CRLF | 字符串 | WINDOWS | 用于在每个记录后添加回车的字符(可选)。可能的值 = WINDOWS、UNIX、MAC 或自定义.如果您指定了前面列出的三个值,则您输入的值(custom)将用作 CRLF 字符。 | |
| isOrdered | 布尔值 | false | 指明消息是否在输出中排序。此注解与模型的消息类关联,必须被声明一次。 | |
| name | 字符串 | 描述消息的名称(可选) | ||
| type | 字符串 | FIX | 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(keyValuePairSeparator = "=", pairSeparator = "\u0001", type="FIX", version="4.1")
public class Order {
}
查看测试问题单
ASCII 字符,如 tab, … 不能显示在 WIKI 页面中。因此,查看 camel-bindy 的测试案例,以了解 FIX 消息如何类似(https://github.com/apache/camel/blob/main/components/camel-bindy/src/test/data/fix/fix.txt)和 Order, Trailer, Header 类(https://github.com/apache/camel/blob/main/components/camel-bindy/src/test/java/org/apache/camel/dataformat/bindy/model/fix/simple/Order.java)。
20.3.6. 6.KeyValuePairField 复制链接链接已复制到粘贴板!
KeyValuePairField 注释定义键值对字段的属性。每个 KeyValuePairField 都通过标签(= 键)及其值关联,一个类型(字符串,int、date、…)、可选y 特征,以及是否需要该字段。
| 注解名称 | 记录类型 | 级别 |
|---|---|---|
| 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 | 字符串 | 要使用的时区。 |
case 1 : tag
这个参数代表消息中字段的键:
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;
}
20.3.7. 7.部分 复制链接链接已复制到粘贴板!
在固定长度记录的 FIX 消息中,在信息 : header, body 和 section 的表示中通常会有不同的部分。注释 @Section 的目的是告知绑定哪个类模型代表标头(= 第 1 节)、正文(= 第 2 节)和页脚(= 第 3 节)
此注释仅存在一个属性/参数。
| 注解名称 | 记录类型 | 级别 |
|---|---|---|
| 部分 | FIX | 类 |
| 参数名称 | 类型 | 必填 | 默认值 | 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;
}
20.3.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;
}
case 2:阅读包含标签/密钥组的 FIX 消息
以下是我们在我们的模型中要处理的信息:
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;
}
20.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();
}
}
20.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);
}
};
}
}