jPOS中F35 Track 2字段含CHAR分隔符的BCD打包问题咨询
解答你的jPOS Track2字段配置与分隔符问题
首先,先澄清你对IFB_LLHNUM的理解偏差:
- 你的部分理解是对的:
B代表二进制BCD打包,H表示用1字节二进制传输长度,NUM表示处理数字型数据。 - 但**
LL并不是指最大长度37**:jPOS字段类型命名里的LL是指长度字段本身占2字节,而你需要的是1字节长度字段,所以应该用IFB_HNUM而非IFB_LLHNUM——这也是配置里的第一个小问题,规范明确是1字节二进制长度,替换成org.jpos.iso.IFB_HNUM才符合要求。
接下来解决核心问题:Track2分隔符的正确编码。
规范里说Track2的分隔符^要编码为二进制X'D'(也就是BCD编码中的13,十六进制D),但IFB_HNUM这类默认的数字型打包器只会处理0-9的字符,遇到D会把它当成普通字符错误转换(就像你看到的D变成了7)。这里有两种可行的解决方案:
方案1:手动转换为BCD字节数组后设置字段
这种方法不需要修改Packager配置,直接在代码层处理字符串到BCD字节数组的转换:
- 写一个工具方法,把包含
D(或^)的Track2字符串转换成符合要求的BCD字节数组:
private static byte[] convertTrack2ToBCD(String track2Str) throws ISOException { int strLength = track2Str.length(); int bcdLength = (strLength + 1) / 2; byte[] bcdData = new byte[bcdLength]; for (int i = 0, idx = 0; i < strLength; i += 2, idx++) { // 处理第一个字符(高4位) char c1 = track2Str.charAt(i); int val1 = Character.digit(c1, 16); if (val1 == -1) { // 如果是^,转换为BCD的D(13) val1 = c1 == '^' ? 13 : -1; if (val1 == -1) throw new ISOException("Track2包含无效字符: " + c1); } // 处理第二个字符(低4位,若存在) int val2 = 0; if (i + 1 < strLength) { char c2 = track2Str.charAt(i + 1); val2 = Character.digit(c2, 16); if (val2 == -1) { val2 = c2 == '^' ? 13 : -1; if (val2 == -1) throw new ISOException("Track2包含无效字符: " + c2); } } bcdData[idx] = (byte) ((val1 << 4) | val2); } return bcdData; }
- 在你的业务代码里,调用这个方法转换后再设置字段:
// 替换原来的msg.set("35", "...") String track2 = "4123456891023456D49121010000020100000"; try { byte[] track2BCD = convertTrack2ToBCD(track2); msg.set("35", track2BCD); } catch (ISOException e) { // 处理无效字符异常 e.printStackTrace(); }
方案2:自定义ISOFieldPackager(更优雅的长期方案)
如果你的项目经常需要处理Track2字段,自定义一个打包器可以避免重复的手动转换:
- 继承
IFB_HNUM,重写pack和unpack方法,支持解析D或^为BCD的X'D':
public class IFB_HNUM_Track2 extends IFB_HNUM { public IFB_HNUM_Track2() { super(); } public IFB_HNUM_Track2(int len, String description) { super(len, description); } @Override public byte[] pack(ISOComponent c) throws ISOException { String track2Str = (String) c.getValue(); byte[] bcdData = convertTrack2ToBCD(track2Str); int actualDigitLength = track2Str.length(); // 构建最终报文:1字节长度 + BCD数据 byte[] result = new byte[1 + bcdData.length]; result[0] = (byte) actualDigitLength; System.arraycopy(bcdData, 0, result, 1, bcdData.length); return result; } @Override public int unpack(ISOComponent c, byte[] b, int offset) throws ISOException { int digitLength = b[offset] & 0xFF; byte[] bcdData = new byte[(digitLength + 1) / 2]; System.arraycopy(b, offset + 1, bcdData, 0, bcdData.length); StringBuilder track2Builder = new StringBuilder(digitLength); for (byte bcdByte : bcdData) { int highNibble = (bcdByte >> 4) & 0x0F; int lowNibble = bcdByte & 0x0F; track2Builder.append(Integer.toHexString(highNibble).toUpperCase()); if (track2Builder.length() < digitLength) { track2Builder.append(Integer.toHexString(lowNibble).toUpperCase()); } } // 可以根据需求把D替换回^,或者保留D String track2Str = track2Builder.toString().replace('D', '^'); c.setValue(track2Str); return 1 + bcdData.length; } private byte[] convertTrack2ToBCD(String track2Str) throws ISOException { // 复用方案1里的工具方法逻辑 int strLength = track2Str.length(); int bcdLength = (strLength + 1) / 2; byte[] bcdData = new byte[bcdLength]; for (int i = 0, idx = 0; i < strLength; i += 2, idx++) { char c1 = track2Str.charAt(i); int val1 = Character.digit(c1, 16); if (val1 == -1) { val1 = c1 == '^' ? 13 : -1; if (val1 == -1) throw new ISOException("Track2包含无效字符: " + c1); } int val2 = 0; if (i + 1 < strLength) { char c2 = track2Str.charAt(i + 1); val2 = Character.digit(c2, 16); if (val2 == -1) { val2 = c2 == '^' ? 13 : -1; if (val2 == -1) throw new ISOException("Track2包含无效字符: " + c2); } } bcdData[idx] = (byte) ((val1 << 4) | val2); } return bcdData; } }
- 在你的GenericPackager XML配置里,替换原来的类:
<isofield id="35" length="37" name="TRACK 2 DATA" pad="true" class="com.yourcompany.IFB_HNUM_Track2"/>
- 之后你就可以像原来一样直接设置字符串了,自定义打包器会自动处理分隔符:
msg.set("35","4123456891023456D49121010000020100000"); // 或者用^作为分隔符也可以:msg.set("35","4123456891023456^49121010000020100000");
最后再提醒一下:记得把原来的IFB_LLHNUM换成IFB_HNUM(或者自定义的IFB_HNUM_Track2),因为你的规范是1字节二进制长度,LLHNUM的长度字段是2字节,不符合要求。
内容的提问来源于stack exchange,提问作者tytyty
相关产品推荐
相关产品推荐

