J8583包组合字段处理优化

2023-10-29 05:30
文章标签 组合 优化 处理 j8583

本文主要是介绍J8583包组合字段处理优化,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

最近研究ISO8583域时,采用了大佬编写的J8583公共jar包。不过对于需求中域的组合字段,压缩变长数据长度值时候,j8583不能把子域的长度位一起压缩。修改了j8583,以供参考。

需求:

域63
自定义域(Reserved Private)

变量属性
ANS…163(LLLVAR),3个字节的长度值+最大163个字节的数据。
压缩时用右靠BCD码表示的2个字节的长度值+用ASCII码表示的最大163个字节的数据。

域描述
该域为自定义域,划分成两个子域,格式定义如下:
—— 数据元长度 N3
—— 63.1 自定义域1 AN3
—— 63.2 自定义域2 ANS…120(LLLVAR)

63.1自定义域1
用法一:国际信用卡公司代码
交易响应消息中POS中心返回国际信用卡公司代码;POS上送的通知消息和离线交易,需上送国际信用卡公司代码。国际信用卡公司代码为:
表50 63.1域用法一
国际信用卡公司中文 国际信用卡公司英文 3 位代码
人民币卡 China Union Pay CUP
威士卡 VISA VIS
万事达卡 Master Card MCC
万事达卡 Maestro Card MAE
JCB卡 JCB JCB
大来卡 Dinner Club DCC
运通卡 American Express AEX

用法二:操作员代码
表示POS终端操作员代码,用作在POS签到和批结算交易中上送到POS中心,应答时原路返回。

63.2 自定义域2
该域为一变长域,长度值由右靠的BCD码表示,最大到120个字节。
该域由四个子域构成,具体描述如下:
—— 63.2.1 发卡方保留域 ANS…20(LLVAR)
—— 63.2.2 中国银联保留域 ANS…20(LLVAR)
—— 63.2.3 受理机构保留域 ANS…20(LLVAR)
—— 63.2.4 POS终端保留域 ANS…60(LLVAR)
上述四个子域顺序排列,若后续子域出现而前面子域没有取值,那么前面子域以空格填充。

解析

63域,定义格式如下
在这里插入图片描述

修改对比如下:

在这里插入图片描述

LlvarParseInfo.java

/*
j8583 A Java implementation of the ISO8583 protocol
Copyright (C) 2011 Enrique Zamudio LopezThis library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
*/
package com.swiftplus.posservice.prepose.iso8583.parse;import java.io.UnsupportedEncodingException;
import java.text.ParseException;import com.swiftplus.posservice.prepose.iso8583.CustomBinaryField;
import com.swiftplus.posservice.prepose.iso8583.CustomField;
import com.swiftplus.posservice.prepose.iso8583.IsoType;
import com.swiftplus.posservice.prepose.iso8583.IsoValue;
import com.swiftplus.posservice.prepose.iso8583.util.Bcd;/** This class is used to parse fields of type LLVAR.* * @author Enrique Zamudio*/
public class LlvarParseInfo extends FieldParseInfo {public LlvarParseInfo() {super(IsoType.LLVAR, 0);}@Overridepublic <T> IsoValue<?> parse(final int field, final byte[] buf,final int pos, final CustomField<T> custom)throws ParseException, UnsupportedEncodingException {if (pos < 0) {throw new ParseException(String.format("Invalid LLVAR field %d %d", field, pos), pos);} else if (pos+2 > buf.length) {throw new ParseException(String.format("Insufficient data for LLVAR header, pos %d", pos), pos);}final int len = decodeLength(buf, pos, 2);if (len < 0) {throw new ParseException(String.format("Invalid LLVAR length %d, field %d pos %d", len, field, pos), pos);} else if (len+pos+2 > buf.length) {throw new ParseException(String.format("Insufficient data for LLVAR field %d, pos %d len %d",field, pos, len), pos);}String _v;try {_v = len == 0 ? "" : new String(buf, pos + 2, len, getCharacterEncoding());} catch (IndexOutOfBoundsException ex) {throw new ParseException(String.format("Insufficient data for LLVAR header, field %d pos %d len %d",field, pos, len), pos);}//This is new: if the String's length is different from the specified// length in the buffer, there are probably some extended characters.// So we create a String from the rest of the buffer, and then cut it to// the specified length.if (_v.length() != len) {_v = new String(buf, pos + 2, buf.length-pos-2,getCharacterEncoding()).substring(0, len);}if (custom == null) {return new IsoValue<>(type, _v, len, null);} else {T dec = custom.decodeField(_v);return dec == null ? new IsoValue<>(type, _v, len, null) :new IsoValue<>(type, dec, len, custom);}}@Overridepublic <T> IsoValue<?> parseBinary(final int field, final byte[] buf,final int pos, final CustomField<T> custom)throws ParseException, UnsupportedEncodingException {return parseBinary(field,buf,pos,(CustomBinaryField<T>) custom);}public <T> IsoValue<?> parseBinary(final int field, final byte[] buf,final int pos, final CustomBinaryField<T> custom)throws ParseException, UnsupportedEncodingException {if (pos < 0) {throw new ParseException(String.format("Invalid bin LLVAR field %d pos %d",field, pos), pos);} else if (pos+1 > buf.length) {throw new ParseException(String.format("Insufficient data for bin LLVAR header, field %d pos %d",field, pos), pos);}final int len = Bcd.parseBcdLength(buf[pos]);if (len < 0) {throw new ParseException(String.format("Invalid bin LLVAR length %d, field %d pos %d", len, field, pos), pos);}if (len+pos+1 > buf.length) {throw new ParseException(String.format("Insufficient data for bin LLVAR field %d, pos %d", field, pos), pos);}if (custom == null) {return new IsoValue<>(type, new String(buf, pos + 1, len,getCharacterEncoding()), null);} else {//组合字段改造
//            T dec = custom.decodeField(new String(buf, pos + 1, len, getCharacterEncoding()));T dec = custom.decodeBinaryField(new String(buf, pos + 1, len, getCharacterEncoding()).getBytes("ASCII"),0,len);return dec == null ? new IsoValue<>(type,new String(buf, pos + 1, len, getCharacterEncoding()), null) :new IsoValue<>(type, dec, custom);}}}

LllvarParseInfo.java

/*
j8583 A Java implementation of the ISO8583 protocol
Copyright (C) 2011 Enrique Zamudio LopezThis library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
*/
package com.swiftplus.posservice.prepose.iso8583.parse;import java.io.UnsupportedEncodingException;
import java.text.ParseException;import com.swiftplus.posservice.prepose.iso8583.CustomBinaryField;
import com.swiftplus.posservice.prepose.iso8583.CustomField;
import com.swiftplus.posservice.prepose.iso8583.IsoType;
import com.swiftplus.posservice.prepose.iso8583.IsoValue;
import com.swiftplus.posservice.prepose.iso8583.util.Bcd;/** This class is used to parse fields of type LLLVAR.* * @author Enrique Zamudio*/
public class LllvarParseInfo extends FieldParseInfo {public LllvarParseInfo() {super(IsoType.LLLVAR, 0);}public <T> IsoValue<?> parse(final int field, final byte[] buf,final int pos, final CustomField<T> custom)throws ParseException, UnsupportedEncodingException {if (pos < 0) {throw new ParseException(String.format("Invalid LLLVAR field %d pos %d",field, pos), pos);} else if (pos+3 > buf.length) {throw new ParseException(String.format("Insufficient data for LLLVAR header field %d pos %d", field, pos), pos);}final int len = decodeLength(buf, pos, 3);if (len < 0) {throw new ParseException(String.format("Invalid LLLVAR length %d(%s) field %d pos %d",len, new String(buf, pos, 3), field, pos), pos);} else if (len+pos+3 > buf.length) {throw new ParseException(String.format("Insufficient data for LLLVAR field %d, pos %d len %d",field, pos, len), pos);}String _v;try {_v = len == 0 ? "" : new String(buf, pos + 3, len, getCharacterEncoding());} catch (IndexOutOfBoundsException ex) {throw new ParseException(String.format("Insufficient data for LLLVAR header, field %d pos %d len %d", field, pos, len), pos);}//This is new: if the String's length is different from the specified length in the//buffer, there are probably some extended characters. So we create a String from//the rest of the buffer, and then cut it to the specified length.if (_v.length() != len) {_v = new String(buf, pos + 3, buf.length-pos-3,getCharacterEncoding()).substring(0, len);}if (custom == null) {return new IsoValue<>(type, _v, len, null);} else {T decoded = custom.decodeField(_v);//If decode fails, return string; otherwise use the decoded object and its codecreturn decoded == null ? new IsoValue<>(type, _v, len, null) :new IsoValue<>(type, decoded, len, custom);}}public <T> IsoValue<?> parseBinary(final int field, final byte[] buf,final int pos, final CustomField<T> custom)throws ParseException, UnsupportedEncodingException {return parseBinary(field,buf,pos,(CustomBinaryField<T>) custom);}public <T> IsoValue<?> parseBinary(final int field, final byte[] buf,final int pos, final CustomBinaryField<T> custom)throws ParseException, UnsupportedEncodingException {if (pos < 0) {throw new ParseException(String.format("Invalid bin LLLVAR field %d pos %d", field, pos), pos);} else if (pos+2 > buf.length) {throw new ParseException(String.format("Insufficient data for bin LLLVAR header, field %d pos %d", field, pos), pos);}final int len = ((buf[pos] & 0x0f) * 100) + Bcd.parseBcdLength(buf[pos + 1]);if (len < 0) {throw new ParseException(String.format("Invalid bin LLLVAR length %d, field %d pos %d", len, field, pos), pos);} else if (len+pos+2 > buf.length) {throw new ParseException(String.format("Insufficient data for bin LLLVAR field %d, pos %d", field, pos), pos);}if (custom == null) {return new IsoValue<>(type, new String(buf, pos + 2, len, getCharacterEncoding()), null);} else {//组合字段改造
//			IsoValue<T> v = new IsoValue<>(type, custom.decodeField(
//					new String(buf, pos + 2, len, getCharacterEncoding())), custom);IsoValue<T> v = new IsoValue<>(type, custom.decodeBinaryField(new String(buf, pos + 2, len, getCharacterEncoding()).getBytes("ASCII"),0,len), custom);if (v.getValue() == null) {return new IsoValue<>(type,new String(buf, pos + 2, len, getCharacterEncoding()), null);}return v;}}}

LlllvarParseInfo.java

/** j8583 A Java implementation of the ISO8583 protocol* Copyright (C) 2007 Enrique Zamudio Lopez** This library is free software; you can redistribute it and/or* modify it under the terms of the GNU Lesser General Public* License as published by the Free Software Foundation; either* version 3 of the License, or (at your option) any later version.** This library is distributed in the hope that it will be useful,* but WITHOUT ANY WARRANTY; without even the implied warranty of* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU* Lesser General Public License for more details.** You should have received a copy of the GNU Lesser General Public* License along with this library; if not, write to the Free Software* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA*/
package com.swiftplus.posservice.prepose.iso8583.parse;import com.swiftplus.posservice.prepose.iso8583.CustomBinaryField;
import com.swiftplus.posservice.prepose.iso8583.CustomField;
import com.swiftplus.posservice.prepose.iso8583.IsoType;
import com.swiftplus.posservice.prepose.iso8583.IsoValue;
import com.swiftplus.posservice.prepose.iso8583.util.Bcd;import java.io.UnsupportedEncodingException;
import java.text.ParseException;/*** Blabla.** @author Enrique Zamudio*         Date: 19/02/15 18:30*/
public class LlllvarParseInfo  extends FieldParseInfo {public LlllvarParseInfo() {super(IsoType.LLLLVAR, 0);}@Overridepublic <T> IsoValue<?> parse(final int field, final byte[] buf,final int pos, final CustomField<T> custom)throws ParseException, UnsupportedEncodingException {if (pos < 0) {throw new ParseException(String.format("Invalid LLLLVAR field %d %d", field, pos), pos);} else if (pos+4 > buf.length) {throw new ParseException(String.format("Insufficient data for LLLLVAR header, pos %d", pos), pos);}final int len = decodeLength(buf, pos, 4);if (len < 0) {throw new ParseException(String.format("Invalid LLLLVAR length %d, field %d pos %d", len, field, pos), pos);} else if (len+pos+4 > buf.length) {throw new ParseException(String.format("Insufficient data for LLLLVAR field %d, pos %d", field, pos), pos);}String _v;try {_v = len == 0 ? "" : new String(buf, pos + 4, len, getCharacterEncoding());} catch (IndexOutOfBoundsException ex) {throw new ParseException(String.format("Insufficient data for LLLLVAR header, field %d pos %d", field, pos), pos);}//This is new: if the String's length is different from the specified// length in the buffer, there are probably some extended characters.// So we create a String from the rest of the buffer, and then cut it to// the specified length.if (_v.length() != len) {_v = new String(buf, pos + 4, buf.length-pos-4,getCharacterEncoding()).substring(0, len);}if (custom == null) {return new IsoValue<>(type, _v, len, null);} else {T dec = custom.decodeField(_v);return dec == null ? new IsoValue<>(type, _v, len, null) :new IsoValue<>(type, dec, len, custom);}}@Overridepublic <T> IsoValue<?> parseBinary(final int field, final byte[] buf,final int pos, final CustomField<T> custom)throws ParseException, UnsupportedEncodingException {return parseBinary(field,buf,pos,(CustomBinaryField<T>) custom);}public <T> IsoValue<?> parseBinary(final int field, final byte[] buf,final int pos, final CustomBinaryField<T> custom)throws ParseException, UnsupportedEncodingException {if (pos < 0) {throw new ParseException(String.format("Invalid bin LLLLVAR field %d pos %d",field, pos), pos);} else if (pos+2 > buf.length) {throw new ParseException(String.format("Insufficient data for bin LLLLVAR header, field %d pos %d",field, pos), pos);}final int len = Bcd.parseBcdLength2bytes(buf, pos);if (len < 0) {throw new ParseException(String.format("Invalid bin LLLLVAR length %d, field %d pos %d", len, field, pos), pos);}if (len+pos+2 > buf.length) {throw new ParseException(String.format("Insufficient data for bin LLLLVAR field %d, pos %d", field, pos), pos);}if (custom == null) {return new IsoValue<>(type, new String(buf, pos + 2, len,getCharacterEncoding()), null);} else {//组合字段改造
//            T dec = custom.decodeField(new String(buf, pos + 2, len, getCharacterEncoding()));T dec = custom.decodeBinaryField(new String(buf, pos + 2, len, getCharacterEncoding()).getBytes("ASCII"),0,len);return dec == null ? new IsoValue<>(type,new String(buf, pos + 2, len, getCharacterEncoding()), null) :new IsoValue<>(type, dec, custom);}}}

MessageFactory.java

/*
j8583 A Java implementation of the ISO8583 protocol
Copyright (C) 2007 Enrique Zamudio LopezThis library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
*/
package com.swiftplus.posservice.prepose.iso8583;import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.text.ParseException;
import java.util.*;import com.swiftplus.posservice.prepose.iso8583.parse.DateTimeParseInfo;
import com.swiftplus.posservice.prepose.iso8583.util.Bcd;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import com.swiftplus.posservice.prepose.iso8583.parse.ConfigParser;
import com.swiftplus.posservice.prepose.iso8583.parse.FieldParseInfo;/** This class is used to create messages, either from scratch or from an existing String or byte* buffer. It can be configured to put default values on newly created messages, and also to know* what to expect when reading messages from an InputStream.* <P>* The factory can be configured to know what values to set for newly created messages, both from* a template (useful for fields that must be set with the same value for EVERY message created)* and individually (for trace [field 11] and message date [field 7]).* <P>* It can also be configured to know what fields to expect in incoming messages (all possible values* must be stated, indicating the date type for each). This way the messages can be parsed from* a byte buffer.* * @author Enrique Zamudio*/
public class MessageFactory<T extends IsoMessage> {protected final Logger log = LoggerFactory.getLogger(getClass());/** This map stores the message template for each message type. */private Map<Integer, T> typeTemplates = new HashMap<>();/** Stores the information needed to parse messages sorted by type. */protected Map<Integer, Map<Integer, FieldParseInfo>> parseMap = new HashMap<>();/** Stores the field numbers to be parsed, in order of appearance. */protected Map<Integer, List<Integer>> parseOrder = new HashMap<>();private TraceNumberGenerator traceGen;/** The ISO header to be included in each message type. */private Map<Integer, String> isoHeaders = new HashMap<>();private Map<Integer, byte[]> binIsoHeaders = new HashMap<>();/** A map for the custom field encoder/decoders, keyed by field number. */@SuppressWarnings("rawtypes")private Map<Integer, CustomField> customFields = new HashMap<>();/** Indicates if the current date should be set on new messages (field 7). */private boolean setDate;/** Indicates that the header should be written/parsed as binary */private boolean binaryHeader;/** Indicates that the fields should be written/parsed as binary */private boolean binaryFields;private int etx = -1;/** Flag to specify if missing fields should be ignored as long as they're at* the end of the message. */private boolean ignoreLast;private boolean forceb2;private boolean binBitmap;private boolean forceStringEncoding;private String encoding = System.getProperty("file.encoding");/** This flag gets passed on to newly created messages and also sets this value for all* field parsers in parsing guides. */public void setForceStringEncoding(boolean flag) {forceStringEncoding = flag;for (Map<Integer,FieldParseInfo> pm : parseMap.values()) {for (FieldParseInfo parser : pm.values()) {parser.setForceStringDecoding(flag);}}}public boolean isForceStringEncoding() {return forceStringEncoding;}/** Tells the factory to create messages that encode their bitmaps in binary format* even when they're encoded as text. Has no effect on binary messages. */public void setUseBinaryBitmap(boolean flag) {binBitmap = flag;}/** Returns true if the factory is set to create and parse bitmaps in binary format* when the messages are encoded as text. */public boolean isUseBinaryBitmap() {return binBitmap;}/** Sets the character encoding used for parsing ALPHA, LLVAR and LLLVAR fields. */public void setCharacterEncoding(String value) {if (encoding == null) {throw new IllegalArgumentException("Cannot set null encoding.");}encoding = value;if (!parseMap.isEmpty()) {for (Map<Integer, FieldParseInfo> pt : parseMap.values()) {for (FieldParseInfo fpi : pt.values()) {fpi.setCharacterEncoding(encoding);}}}if (!typeTemplates.isEmpty()) {for (T tmpl : typeTemplates.values()) {tmpl.setCharacterEncoding(encoding);for (int i = 2 ; i<129; i++) {IsoValue<?> v = tmpl.getField(i);if (v != null) {v.setCharacterEncoding(encoding);}}}}}/** Returns the encoding used to parse ALPHA, LLVAR and LLLVAR fields. The default is the* file.encoding system property. */public String getCharacterEncoding() {return encoding;}/** Sets or clears the flag to pass to new messages, to include a secondary bitmap* even if it's not needed. */public void setForceSecondaryBitmap(boolean flag) {forceb2 = flag;}public boolean isForceSecondaryBitmap() {return forceb2;}/** Setting this property to true avoids getting a ParseException when parsing messages that don't have* the last field specified in the bitmap. This is common with certain providers where field 128 is* specified in the bitmap but not actually included in the messages. Default is false, which has* been the behavior in previous versions when this option didn't exist. */public void setIgnoreLastMissingField(boolean flag) {ignoreLast = flag;}/** This flag indicates if the MessageFactory throws an exception if the last field of a message* is not really present even though it's specified in the bitmap. Default is false which means* an exception is thrown. */public boolean getIgnoreLastMissingField() {return ignoreLast;}/** Specifies a map for custom field encoder/decoders. The keys are the field numbers. */@SuppressWarnings("rawtypes")public void setCustomFields(Map<Integer, CustomField> value) {customFields = value;}/** Sets the CustomField encoder for the specified field number. */public void setCustomField(int index, CustomField<?> value) {customFields.put(index, value);}/** Returns a custom field encoder/decoder for the specified field number, if one is available. */@SuppressWarnings("unchecked")public <F> CustomField<F> getCustomField(int index) {return customFields.get(index);}/** Returns a custom field encoder/decoder for the specified field number, if one is available. */@SuppressWarnings("unchecked")public <F> CustomField<F> getCustomField(Integer index) {return customFields.get(index);}/** Tells the receiver to read the configuration at the specified path. This just calls* ConfigParser.configureFromClasspathConfig() with itself and the specified path at arguments,* but is really convenient in case the MessageFactory is being configured from within, say, Spring. */public void setConfigPath(String path) throws IOException {ConfigParser.configureFromClasspathConfig(this, path);//Now re-set some properties that need to be propagated down to the recently assigned objectssetCharacterEncoding(encoding);setForceStringEncoding(forceStringEncoding);}/** Tells the receiver to create and parse binary messages if the flag is true.* Default is false, that is, create and parse ASCII messages. Sets both binaryHeader and fields to the flag.*/public void setUseBinaryMessages(boolean flag) {binaryHeader = binaryFields = flag;}/** Returns true is the factory is set to create and parse binary messages,* false if it uses ASCII messages. Default is false. True if both binaryHeader &amp; binaryFields* are set to true* @deprecated Check the new flags binaryHeader and binaryFields instead.*/@Deprecatedpublic boolean getUseBinaryMessages() {return binaryHeader && binaryFields;}/** header portion of the message is written/parsed in binary, default is false */public void setBinaryHeader(boolean flag){binaryHeader = flag;}/** header portion of the message is written/parsed in binary, default is false */public boolean isBinaryHeader(){return binaryHeader;}/** fields portion of the message is written/parsed in binary, default is false */public void setBinaryFields(boolean flag){binaryFields = flag;}/** fields portion of the message is written/parsed in binary, default is false */public boolean isBinaryFields(){return binaryFields;}/** fields portion of the message is written/parsed in binary *//** Sets the ETX character to be sent at the end of the message. This is optional and the* default is -1, which means nothing should be sent as terminator.* @param value The ASCII value of the ETX character or -1 to indicate no terminator should be used. */public void setEtx(int value) {etx = value;}public int getEtx() {return etx;}/** Creates a new message of the specified type, with optional trace and date values as well* as any other values specified in a message template. If the factory is set to use binary* messages, then the returned message will be written using binary coding.* @param type The message type, for example 0x200, 0x400, etc. */public T newMessage(int type) {T m;if (binIsoHeaders.get(type) != null) {m = createIsoMessageWithBinaryHeader(binIsoHeaders.get(type));} else {m = createIsoMessage(isoHeaders.get(type));}m.setType(type);m.setEtx(etx);m.setBinaryHeader(isBinaryHeader());m.setBinaryFields(isBinaryFields());m.setForceSecondaryBitmap(forceb2);m.setBinaryBitmap(binBitmap);m.setCharacterEncoding(encoding);m.setForceStringEncoding(forceStringEncoding);//Copy the values from the templateIsoMessage templ = typeTemplates.get(type);if (templ != null) {for (int i = 2; i <= 128; i++) {if (templ.hasField(i)) {//We could detect here if there's a custom object with a CustomField,//but we can't copy the value so there's no point.m.setField(i, templ.getField(i).clone());}}}if (traceGen != null) {m.setValue(11, traceGen.nextTrace(), IsoType.NUMERIC, 6);}if (setDate) {if (m.hasField(7)) {//We may have a field with a timezone but no valuem.updateValue(7, new Date());} else {IsoValue<Date> now = new IsoValue<>(IsoType.DATE10, new Date());if (DateTimeParseInfo.getDefaultTimeZone() != null) {now.setTimeZone(DateTimeParseInfo.getDefaultTimeZone());}m.setField(7, now);}}return m;}/** Creates a message to respond to a request. Increments the message type by 16,* sets all fields from the template if there is one, and copies all values from the request,* overwriting fields from the template if they overlap.* @param request An ISO8583 message with a request type (ending in 00). */public T createResponse(T request) {T resp = createIsoMessage(isoHeaders.get(request.getType() + 16));resp.setCharacterEncoding(request.getCharacterEncoding());resp.setBinaryHeader(request.isBinaryHeader());resp.setBinaryFields(request.isBinaryFields());resp.setBinaryBitmap(request.isBinaryBitmap());resp.setType(request.getType() + 16);resp.setEtx(etx);resp.setForceSecondaryBitmap(forceb2);//Copy the values from the template or the request (request has preference)IsoMessage templ = typeTemplates.get(resp.getType());if (templ == null) {for (int i = 2; i < 128; i++) {if (request.hasField(i)) {resp.setField(i, request.getField(i).clone());}}} else {for (int i = 2; i < 128; i++) {if (request.hasField(i)) {resp.setField(i, request.getField(i).clone());} else if (templ.hasField(i)) {resp.setField(i, templ.getField(i).clone());}}}return resp;}/** Sets the timezone for the specified FieldParseInfo, if it's needed for parsing dates. */public void setTimezoneForParseGuide(int messageType, int field, TimeZone tz) {if (field == 0) {DateTimeParseInfo.setDefaultTimeZone(tz);}Map<Integer, FieldParseInfo> guide = parseMap.get(messageType);if (guide != null) {FieldParseInfo fpi = guide.get(field);if (fpi instanceof DateTimeParseInfo) {((DateTimeParseInfo) fpi).setTimeZone(tz);return;}}log.warn("Field {} for message type {} is not for dates, cannot set timezone",field, messageType);}/** Convenience for parseMessage(buf, isoHeaderLength, false) */public T parseMessage(byte[] buf, int isoHeaderLength)throws ParseException, UnsupportedEncodingException {return parseMessage(buf, isoHeaderLength, false);}/** Creates a new message instance from the buffer, which must contain a valid ISO8583* message. If the factory is set to use binary messages then it will try to parse* a binary message.* @param buf The byte buffer containing the message. Must not include the length header.* @param isoHeaderLength The expected length of the ISO header, after which the message type* and the rest of the message must come. */public T parseMessage(byte[] buf, int isoHeaderLength, boolean binaryIsoHeader)throws ParseException, UnsupportedEncodingException {final int minlength = isoHeaderLength+(binaryHeader?2:4)+(binBitmap||binaryHeader ? 8:16);if (buf.length < minlength) {throw new ParseException("Insufficient buffer length, needs to be at least " + minlength, 0);}final T m;if (binaryIsoHeader && isoHeaderLength > 0) {byte[] _bih = new byte[isoHeaderLength];System.arraycopy(buf, 0, _bih, 0, isoHeaderLength);m = createIsoMessageWithBinaryHeader(_bih);} else {m = createIsoMessage(isoHeaderLength > 0 ?new String(buf, 0, isoHeaderLength, encoding) : null);}m.setCharacterEncoding(encoding);final int type;if (binaryHeader) {type = ((buf[isoHeaderLength] & 0xff) << 8) | (buf[isoHeaderLength + 1] & 0xff);} else if (forceStringEncoding) {type = Integer.parseInt(new String(buf, isoHeaderLength, 4, encoding), 16);} else {type = ((buf[isoHeaderLength] - 48) << 12)| ((buf[isoHeaderLength + 1] - 48) << 8)| ((buf[isoHeaderLength + 2] - 48) << 4)| (buf[isoHeaderLength + 3] - 48);}m.setType(type);//Parse the bitmap (primary first)final BitSet bs = new BitSet(64);int pos = 0;if (binaryHeader || binBitmap) {final int bitmapStart = isoHeaderLength + (binaryHeader ? 2 : 4);for (int i = bitmapStart; i < 8+bitmapStart; i++) {int bit = 128;for (int b = 0; b < 8; b++) {bs.set(pos++, (buf[i] & bit) != 0);bit >>= 1;}}//Check for secondary bitmap and parse if necessaryif (bs.get(0)) {if (buf.length < minlength + 8) {throw new ParseException("Insufficient length for secondary bitmap", minlength);}for (int i = 8+bitmapStart; i < 16+bitmapStart; i++) {int bit = 128;for (int b = 0; b < 8; b++) {bs.set(pos++, (buf[i] & bit) != 0);bit >>= 1;}}pos = minlength + 8;} else {pos = minlength;}} else {//ASCII parsingtry {final byte[] bitmapBuffer;if (forceStringEncoding) {byte[] _bb = new String(buf, isoHeaderLength+4, 16, encoding).getBytes();bitmapBuffer = new byte[36+isoHeaderLength];System.arraycopy(_bb, 0, bitmapBuffer, 4+isoHeaderLength, 16);} else {bitmapBuffer = buf;}for (int i = isoHeaderLength + 4; i < isoHeaderLength + 20; i++) {if (bitmapBuffer[i] >= '0' && bitmapBuffer[i] <= '9') {bs.set(pos++, ((bitmapBuffer[i] - 48) & 8) > 0);bs.set(pos++, ((bitmapBuffer[i] - 48) & 4) > 0);bs.set(pos++, ((bitmapBuffer[i] - 48) & 2) > 0);bs.set(pos++, ((bitmapBuffer[i] - 48) & 1) > 0);} else if (bitmapBuffer[i] >= 'A' && bitmapBuffer[i] <= 'F') {bs.set(pos++, ((bitmapBuffer[i] - 55) & 8) > 0);bs.set(pos++, ((bitmapBuffer[i] - 55) & 4) > 0);bs.set(pos++, ((bitmapBuffer[i] - 55) & 2) > 0);bs.set(pos++, ((bitmapBuffer[i] - 55) & 1) > 0);} else if (bitmapBuffer[i] >= 'a' && bitmapBuffer[i] <= 'f') {bs.set(pos++, ((bitmapBuffer[i] - 87) & 8) > 0);bs.set(pos++, ((bitmapBuffer[i] - 87) & 4) > 0);bs.set(pos++, ((bitmapBuffer[i] - 87) & 2) > 0);bs.set(pos++, ((bitmapBuffer[i] - 87) & 1) > 0);}}//Check for secondary bitmap and parse it if necessaryif (bs.get(0)) {if (buf.length < minlength + 16) {throw new ParseException("Insufficient length for secondary bitmap", minlength);}if (forceStringEncoding) {byte[] _bb = new String(buf, isoHeaderLength+20, 16, encoding).getBytes();System.arraycopy(_bb, 0, bitmapBuffer, 20+isoHeaderLength, 16);}for (int i = isoHeaderLength + 20; i < isoHeaderLength + 36; i++) {if (bitmapBuffer[i] >= '0' && bitmapBuffer[i] <= '9') {bs.set(pos++, ((bitmapBuffer[i] - 48) & 8) > 0);bs.set(pos++, ((bitmapBuffer[i] - 48) & 4) > 0);bs.set(pos++, ((bitmapBuffer[i] - 48) & 2) > 0);bs.set(pos++, ((bitmapBuffer[i] - 48) & 1) > 0);} else if (bitmapBuffer[i] >= 'A' && bitmapBuffer[i] <= 'F') {bs.set(pos++, ((bitmapBuffer[i] - 55) & 8) > 0);bs.set(pos++, ((bitmapBuffer[i] - 55) & 4) > 0);bs.set(pos++, ((bitmapBuffer[i] - 55) & 2) > 0);bs.set(pos++, ((bitmapBuffer[i] - 55) & 1) > 0);} else if (bitmapBuffer[i] >= 'a' && bitmapBuffer[i] <= 'f') {bs.set(pos++, ((bitmapBuffer[i] - 87) & 8) > 0);bs.set(pos++, ((bitmapBuffer[i] - 87) & 4) > 0);bs.set(pos++, ((bitmapBuffer[i] - 87) & 2) > 0);bs.set(pos++, ((bitmapBuffer[i] - 87) & 1) > 0);}}pos = 16 + minlength;} else {pos = minlength;}} catch (NumberFormatException ex) {ParseException _e = new ParseException("Invalid ISO8583 bitmap", pos);_e.initCause(ex);throw _e;}}//Parse each fieldMap<Integer, FieldParseInfo> parseGuide = parseMap.get(type);List<Integer> index = parseOrder.get(type);if (index == null) {log.error(String.format("ISO8583 MessageFactory has no parsing guide for message type %04x [%s]",type, new String(buf)));throw new ParseException(String.format("ISO8583 MessageFactory has no parsing guide for message type %04x [%s]",type,new String(buf)), 0);}//First we check if the message contains fields not specified in the parsing templateboolean abandon = false;for (int i = 1; i < bs.length(); i++) {if (bs.get(i) && !index.contains(i+1)) {log.warn("ISO8583 MessageFactory cannot parse field {}: unspecified in parsing guide for type {}",i+1, Integer.toString(type, 16));abandon = true;}}if (abandon) {throw new ParseException("ISO8583 MessageFactory cannot parse fields", 0);}//Now we parse each fieldif (binaryFields) {for (Integer i : index) {FieldParseInfo fpi = parseGuide.get(i);if (bs.get(i - 1)) {if (ignoreLast && pos >= buf.length && i.intValue() == index.get(index.size() -1)) {log.warn("Field {} is not really in the message even though it's in the bitmap", i);bs.clear(i - 1);} else {CustomField<?> decoder = fpi.getDecoder();if (decoder == null) {decoder = getCustomField(i);}IsoValue<?> val = fpi.parseBinary(i, buf, pos, decoder);m.setField(i, val);if (val != null) {if (val.getType() == IsoType.NUMERIC || val.getType() == IsoType.DATE10|| val.getType() == IsoType.DATE4|| val.getType() == IsoType.DATE12|| val.getType() == IsoType.DATE14|| val.getType() == IsoType.DATE_EXP|| val.getType() == IsoType.AMOUNT|| val.getType() == IsoType.TIME) {pos += (val.getLength() / 2) + (val.getLength() % 2);} else if (val.getType() == IsoType.LLBCDBIN || val.getType() == IsoType.LLLBCDBIN || val.getType() == IsoType.LLLLBCDBIN) {pos += val.getLength() / 2 + ((val.getLength() % 2 == 0) ? 0 : 1);} else {//组合字段,长度使用LLVAR中长度位"LL"通过BCD转码后的长度if (decoder != null ){if (val.getType() == IsoType.LLVAR) {pos += Bcd.parseBcdLength(buf[pos]);}if (val.getType() == IsoType.LLLVAR) {pos += ((buf[pos] & 0x0f) * 100) + Bcd.parseBcdLength(buf[pos + 1]);}if (val.getType() == IsoType.LLLLVAR) {pos += Bcd.parseBcdLength2bytes(buf, pos);}}else {pos += val.getLength();}}if (val.getType() == IsoType.LLVAR || val.getType() == IsoType.LLBIN || val.getType() == IsoType.LLBCDBIN ) {pos++;} else if (val.getType() == IsoType.LLLVAR|| val.getType() == IsoType.LLLBIN|| val.getType() == IsoType.LLLBCDBIN|| val.getType() == IsoType.LLLLVAR|| val.getType() == IsoType.LLLLBIN|| val.getType() == IsoType.LLLLBCDBIN) {pos += 2;}}}}}} else {for (Integer i : index) {FieldParseInfo fpi = parseGuide.get(i);if (bs.get(i - 1)) {if (ignoreLast && pos >= buf.length && i.intValue() == index.get(index.size() -1)) {log.warn("Field {} is not really in the message even though it's in the bitmap", i);bs.clear(i - 1);} else {CustomField<?> decoder = fpi.getDecoder();if (decoder == null) {decoder = getCustomField(i);}IsoValue<?> val = fpi.parse(i, buf, pos, decoder);m.setField(i, val);//To get the correct next position, we need to get the number of bytes, not charspos += val.toString().getBytes(fpi.getCharacterEncoding()).length;if (val.getType() == IsoType.LLVAR || val.getType() == IsoType.LLBIN || val.getType() == IsoType.LLBCDBIN) {pos += 2;} else if (val.getType() == IsoType.LLLVAR || val.getType() == IsoType.LLLBIN || val.getType() == IsoType.LLLBCDBIN) {pos += 3;} else if (val.getType() == IsoType.LLLLVAR || val.getType() == IsoType.LLLLBIN || val.getType() == IsoType.LLLLBCDBIN) {pos += 4;}}}}}m.setBinaryHeader(binaryHeader);m.setBinaryFields(binaryFields);m.setBinaryBitmap(binBitmap);return m;}/** Creates a Iso message, override this method in the subclass to provide your * own implementations of IsoMessage.* @param header The optional ISO header that goes before the message type* @return IsoMessage*/@SuppressWarnings("unchecked")protected T createIsoMessage(String header) {return (T)new IsoMessage(header);}/** Creates a Iso message with the specified binary ISO header.* Override this method in the subclass to provide your* own implementations of IsoMessage.* @param binHeader The optional ISO header that goes before the message type* @return IsoMessage*/@SuppressWarnings("unchecked")protected T createIsoMessageWithBinaryHeader(byte[] binHeader) {return (T)new IsoMessage(binHeader);}/** Sets whether the factory should set the current date on newly created messages,* in field 7. Default is false. */public void setAssignDate(boolean flag) {setDate = flag;}/** Returns true if the factory is assigning the current date to newly created messages* (field 7). Default is false. */public boolean getAssignDate() {return setDate;}/** Sets the generator that this factory will get new trace numbers from. There is no* default generator. */public void setTraceNumberGenerator(TraceNumberGenerator value) {traceGen = value;}/** Returns the generator used to assign trace numbers to new messages. */public TraceNumberGenerator getTraceNumberGenerator() {return traceGen;}/** Sets the ISO header to be used in each message type.* @param value A map where the keys are the message types and the values are the ISO headers.*/public void setIsoHeaders(Map<Integer, String> value) {isoHeaders.clear();isoHeaders.putAll(value);}/** Sets the ISO header for a specific message type.* @param type The message type, for example 0x200.* @param value The ISO header, or NULL to remove any headers for this message type. */public void setIsoHeader(int type, String value) {if (value == null) {isoHeaders.remove(type);} else {isoHeaders.put(type, value);binIsoHeaders.remove(type);}}/** Returns the ISO header used for the specified type. */public String getIsoHeader(int type) {return isoHeaders.get(type);}/** Sets the ISO header for a specific message type, in binary format.* @param type The message type, for example 0x200.* @param value The ISO header, or NULL to remove any headers for this message type. */public void setBinaryIsoHeader(int type, byte[] value) {if (value == null) {binIsoHeaders.remove(type);} else {binIsoHeaders.put(type, value);isoHeaders.remove(type);}}/** Returns the binary ISO header used for the specified type. */public byte[] getBinaryIsoHeader(int type) {return binIsoHeaders.get(type);}/** Adds a message template to the factory. If there was a template for the same* message type as the new one, it is overwritten. */public void addMessageTemplate(T templ) {if (templ != null) {typeTemplates.put(templ.getType(), templ);}}/** Removes the message template for the specified type. */public void removeMessageTemplate(int type) {typeTemplates.remove(type);}/** Returns the template for the specified message type. This allows templates to be modified* programmatically. */public T getMessageTemplate(int type) {return typeTemplates.get(type);}/** Invoke this method in case you want to freeze the configuration, making message and parsing* templates, as well as iso headers and custom fields, immutable. */public void freeze() {typeTemplates = Collections.unmodifiableMap(typeTemplates);parseMap = Collections.unmodifiableMap(parseMap);parseOrder = Collections.unmodifiableMap(parseOrder);isoHeaders = Collections.unmodifiableMap(isoHeaders);binIsoHeaders = Collections.unmodifiableMap(binIsoHeaders);customFields = Collections.unmodifiableMap(customFields);}/** Sets a map with the fields that are to be expected when parsing a certain type of* message.* @param type The message type.* @param map A map of FieldParseInfo instances, each of which define what type and length* of field to expect. The keys will be the field numbers. */public void setParseMap(int type, Map<Integer, FieldParseInfo> map) {parseMap.put(type, map);ArrayList<Integer> index = new ArrayList<>();index.addAll(map.keySet());Collections.sort(index);log.trace(String.format("ISO8583 MessageFactory adding parse map for type %04x with fields %s",type, index));parseOrder.put(type, index);}}

IsoValue.java

/*
j8583 A Java implementation of the ISO8583 protocol
Copyright (C) 2007 Enrique Zamudio LopezThis library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
*/
package com.swiftplus.posservice.prepose.iso8583;import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Date;
import java.util.TimeZone;import com.swiftplus.posservice.prepose.iso8583.util.Bcd;
import com.swiftplus.posservice.prepose.iso8583.util.HexCodec;/** Represents a value that is stored in a field inside an ISO8583 message.* It can format the value when the message is generated.* Some values have a fixed length, other values require a length to be specified* so that the value can be padded to the specified length. LLVAR and LLLVAR* values do not need a length specification because the length is calculated* from the stored value.* * @author Enrique Zamudio*/
public class IsoValue<T> implements Cloneable {private IsoType type;private T value;private CustomFieldEncoder<T> encoder;private int length;private String encoding;private TimeZone tz;private boolean binaryField;public IsoValue(IsoType t, T value)  {this(t, value, null);}public IsoValue(IsoType t, T value,boolean binaryFlag)  {this(t, value, null,binaryFlag);}/** Creates a new instance that stores the specified value as the specified type.* Useful for storing LLVAR or LLLVAR types, as well as fixed-length value types* like DATE10, DATE4, AMOUNT, etc.* @param t the ISO type.* @param value The value to be stored.* @param custom An optional CustomFieldEncoder for the value.*/public IsoValue(IsoType t, T value, CustomFieldEncoder<T> custom )  {this(t,value,custom,false);}public IsoValue(IsoType t, T value, CustomFieldEncoder<T> custom,boolean binaryFlag)  {if (t.needsLength()) {throw new IllegalArgumentException("Fixed-value types must use constructor that specifies length");}encoder = custom;type = t;this.value = value;this.binaryField = binaryFlag;if (type == IsoType.LLVAR || type == IsoType.LLLVAR || type == IsoType.LLLLVAR) {if (custom == null) {length = value.toString().length();} else {//二进制判断String enc;if (binaryFlag){try {enc = new String(((CustomBinaryField<T>)custom).encodeBinaryField(value),"UTF-8");} catch (UnsupportedEncodingException e) {enc = null;}}else {enc = custom.encodeField(value);}if (enc == null) {enc = value == null ? "" : value.toString();}length = enc.length();}validateTypeWithVariableLength();} else if (type == IsoType.LLBIN || type == IsoType.LLLBIN || type == IsoType.LLLLBIN) {if (custom == null) {if (value instanceof byte[]) {length = ((byte[])value).length;} else {length = value.toString().length() / 2 + (value.toString().length() % 2);}} else if (custom instanceof CustomBinaryField) {length = ((CustomBinaryField<T>)custom).encodeBinaryField(value).length;} else {//二进制判断String enc;if (binaryFlag){try {enc = new String(((CustomBinaryField<T>)custom).encodeBinaryField(value),"UTF-8");} catch (UnsupportedEncodingException e) {enc = null;}}else {enc = custom.encodeField(value);}if (enc == null) {enc = value == null ? "" : value.toString();}length = enc.length();}validateTypeWithVariableLength();} else if (type == IsoType.LLBCDBIN || type == IsoType.LLLBCDBIN || type == IsoType.LLLLBCDBIN) {if (value instanceof byte[]) {length = ((byte[])value).length * 2;} else {length = value.toString().length();}validateTypeWithVariableLength();} else {length = type.getLength();}}public IsoValue(IsoType t, T val, int len) {this(t, val, len, null);}public IsoValue(IsoType t, T val, int len,boolean binaryFlag) {this(t, val, len, null,binaryFlag);}/** Creates a new instance that stores the specified value as the specified type.* Useful for storing fixed-length value types.* @param t The ISO8583 type for this field.* @param val The value to store in the field.* @param len The length for the value.* @param custom An optional CustomFieldEncoder for the value.*/public IsoValue(IsoType t, T val, int len, CustomFieldEncoder<T> custom) {this(t,val,len,custom,false);}public IsoValue(IsoType t, T val, int len, CustomFieldEncoder<T> custom,boolean binaryFlag) {type = t;value = val;length = len;encoder = custom;this.binaryField = binaryFlag;if (length == 0 && t.needsLength()) {throw new IllegalArgumentException(String.format("Length must be greater than zero for type %s (value '%s')", t, val));} else if (t == IsoType.LLVAR || t == IsoType.LLLVAR || t == IsoType.LLLLVAR) {if (len == 0) {if (binaryFlag){length = custom == null ? val.toString().length() : ((CustomBinaryField<T>)custom).encodeBinaryField(value).length;}else {length = custom == null ? val.toString().length() : custom.encodeField(value).length();}}validateTypeWithVariableLength();} else if (t == IsoType.LLBIN || t == IsoType.LLLBIN || t == IsoType.LLLLBIN) {if (len == 0) {if (custom == null) {length = ((byte[])val).length;} else if (custom instanceof CustomBinaryField ) { //  || binaryFlaglength = ((CustomBinaryField<T>)custom).encodeBinaryField(value).length;} else {length = custom.encodeField(value).length();}if (binaryFlag){length = custom == null ? ((byte[]) val).length : ((CustomBinaryField<T>)custom).encodeBinaryField(value).length;}else {length = custom == null ? ((byte[]) val).length : custom.encodeField(value).length();}}validateTypeWithVariableLength();} else if (t == IsoType.LLBCDBIN || t == IsoType.LLLBCDBIN || t == IsoType.LLLLBCDBIN) {if (len == 0) {if (value instanceof byte[]) {length = ((byte[]) value).length * 2;} else {length = value.toString().length();}}validateTypeWithVariableLength();}}/** Returns the ISO type to which the value must be formatted. */public IsoType getType() {return type;}/** Returns the length of the stored value, of the length of the formatted value* in case of NUMERIC or ALPHA. It doesn't include the field length header in case* of LLVAR or LLLVAR. */public int getLength() {return length;}/** Returns the stored value without any conversion or formatting. */public T getValue() {return value;}public void setCharacterEncoding(String value) {encoding = value;}public String getCharacterEncoding() {return encoding;}/** Sets the timezone, useful for date fields. */public void setTimeZone(TimeZone value) {tz = value;}public TimeZone getTimeZone() {return tz;}/** Returns the formatted value as a String. The formatting depends on the type of the* receiver. */public String toString() {if (value == null) {return "ISOValue<null>";}if (type == IsoType.NUMERIC || type == IsoType.AMOUNT) {if (type == IsoType.AMOUNT) {if (value instanceof BigDecimal) {return type.format((BigDecimal) value, 12);} else {return type.format(value.toString(), 12);}} else if (value instanceof BigInteger) {return type.format(encoder == null ? value.toString() : encoder.encodeField(value), length);} else if (value instanceof Number) {return type.format(((Number)value).longValue(), length);} else {return type.format(encoder == null ? value.toString() : encoder.encodeField(value), length);}} else if (type == IsoType.ALPHA) {return type.format(encoder == null ? value.toString() : encoder.encodeField(value), length);} else if (type == IsoType.LLVAR || type == IsoType.LLLVAR || type == IsoType.LLLLVAR) {
//			return getStringEncoded();return getBinStringEncoded();} else if (value instanceof Date) {return type.format((Date)value, tz);} else if (type == IsoType.BINARY) {if (value instanceof byte[]) {final byte[] _v = (byte[])value;return type.format(encoder == null ? HexCodec.hexEncode(_v, 0, _v.length) : encoder.encodeField(value), length * 2);} else {return type.format(encoder == null ? value.toString() : encoder.encodeField(value), length * 2);}} else if (type == IsoType.LLBIN || type == IsoType.LLLBIN || type == IsoType.LLLLBIN) {if (value instanceof byte[]) {final byte[] _v = (byte[])value;return encoder == null ? HexCodec.hexEncode(_v, 0, _v.length) : encoder.encodeField(value);} else {
//				final String _s = getStringEncoded();final String _s = getBinStringEncoded();return (_s.length() % 2 == 1) ? String.format("0%s", _s) : _s;}} else if (type == IsoType.LLBCDBIN || type == IsoType.LLLBCDBIN || type == IsoType.LLLLBCDBIN) {if (value instanceof byte[]) {final byte[] _v = (byte[])value;final String val = encoder == null ? HexCodec.hexEncode(_v, 0, _v.length) : encoder.encodeField(value);return val.substring(val.length() - length);} else {
//				return getStringEncoded();return getBinStringEncoded();}}return getStringEncoded();}private String getStringEncoded() {return encoder == null ? value.toString() : encoder.encodeField(value);}private String getBinStringEncoded() {if (binaryField){try {return encoder == null ? value.toString() : new String(((CustomBinaryField<T>) encoder).encodeBinaryField(value),encoding == null?"UTF-8":encoding);} catch (UnsupportedEncodingException e) {return "";}}else {return encoder == null ? value.toString() : encoder.encodeField(value);}}/** Returns a copy of the receiver that references the same value object. */@SuppressWarnings("unchecked")public IsoValue<T> clone() {try {return (IsoValue<T>)super.clone();} catch (CloneNotSupportedException ex) {return null;}}/** Returns true of the other object is also an IsoValue and has the same type and length,* and if other.getValue().equals(getValue()) returns true. */public boolean equals(Object other) {if (other == null || !(other instanceof IsoValue<?>)) {return false;}IsoValue<?> comp = (IsoValue<?>)other;return (comp.getType() == getType() && comp.getValue().equals(getValue())&& comp.getLength() == getLength());}@Overridepublic int hashCode() {return value == null ? 0 : toString().hashCode();}/** Returns the CustomFieldEncoder for this value. */public CustomFieldEncoder<T> getEncoder() {return encoder;}protected void writeLengthHeader(final int l, final OutputStream outs, final IsoType type,final boolean binary, final boolean forceStringEncoding)throws IOException {final int digits;if (type == IsoType.LLLLBIN || type == IsoType.LLLLVAR || type == IsoType.LLLLBCDBIN) {digits = 4;} else if (type == IsoType.LLLBIN || type == IsoType.LLLVAR || type == IsoType.LLLBCDBIN) {digits = 3;} else {digits = 2;}if (binary) {if (digits == 4) {outs.write((((l % 10000) / 1000) << 4) | ((l % 1000)/100));} else if (digits == 3) {outs.write(l / 100); //00 to 09 automatically in BCD}//BCD encode the rest of the lengthouts.write((((l % 100) / 10) << 4) | (l % 10));} else if (forceStringEncoding) {String lhead = Integer.toString(l);final int ldiff = digits - lhead.length();if (ldiff == 1) {lhead = '0' + lhead;} else if (ldiff == 2) {lhead = "00" + lhead;} else if (ldiff == 3) {lhead = "000" + lhead;}outs.write(encoding == null ? lhead.getBytes():lhead.getBytes(encoding));} else {//write the length in ASCIIif (digits == 4) {outs.write((l/1000)+48);outs.write(((l%1000)/100)+48);} else if (digits == 3) {outs.write((l / 100) + 48);}if (l >= 10) {outs.write(((l % 100) / 10) + 48);} else {outs.write(48);}outs.write((l % 10) + 48);}}/** Writes the formatted value to a stream, with the length header* if it's a variable length type.* @param outs The stream to which the value will be written.* @param binary Specifies whether the value should be written in binary or text format.* @param forceStringEncoding When using text format, force the encoding of length headers* for variable-length fields to be done with the proper character encoding. When false,* the length headers are encoded as ASCII; this used to be the only behavior. */public void write(final OutputStream outs, final boolean binary, final boolean forceStringEncoding) throws IOException {if (type == IsoType.LLLVAR || type == IsoType.LLVAR || type == IsoType.LLLLVAR) {writeLengthHeader(length, outs, type, binary, forceStringEncoding);} else if (type == IsoType.LLBIN || type == IsoType.LLLBIN || type == IsoType.LLLLBIN) {writeLengthHeader(binary ? length : length*2, outs, type, binary, forceStringEncoding);} else if (type == IsoType.LLBCDBIN || type == IsoType.LLLBCDBIN || type == IsoType.LLLLBCDBIN) {writeLengthHeader(length, outs, type, binary, forceStringEncoding);} else if (binary) {//numeric types in binary are coded like thisbyte[] buf = null;if (type == IsoType.NUMERIC) {buf = new byte[(length / 2) + (length % 2)];} else if (type == IsoType.AMOUNT) {buf = new byte[6];} else if (type == IsoType.DATE10 || type == IsoType.DATE4 ||type == IsoType.DATE_EXP || type == IsoType.TIME ||type == IsoType.DATE12 || type == IsoType.DATE14) {buf = new byte[length / 2];}//Encode in BCD if it's one of these typesif (buf != null) {Bcd.encode(toString(), buf);outs.write(buf);return;}}if (binary && (type == IsoType.BINARY || IsoType.VARIABLE_LENGTH_BIN_TYPES.contains(type))) {int missing;if (value instanceof byte[]) {outs.write((byte[])value);missing = length - ((byte[])value).length;} else if (encoder instanceof CustomBinaryField) {byte[] binval = ((CustomBinaryField<T>) encoder).encodeBinaryField(value);outs.write(binval);missing = length - binval.length;} else {byte[] binval = HexCodec.hexDecode(value.toString());outs.write(binval);missing = length - binval.length;}if (type == IsoType.BINARY && missing > 0) {for (int i = 0; i < missing; i++) {outs.write(0);}}} else {outs.write(encoding == null ? toString().getBytes() : toString().getBytes(encoding));}}private void validateTypeWithVariableLength() {if (type == IsoType.LLVAR && length > 99) {throwIllegalArgumentException(type, 99);} else if (type == IsoType.LLLVAR && length > 999) {throwIllegalArgumentException(type, 999);} else if (type == IsoType.LLLLVAR && length > 9999) {throwIllegalArgumentException(type, 9999);} else if (type == IsoType.LLBIN && length > 99) {throwIllegalArgumentException(type, 99);} else if (type == IsoType.LLLBIN && length > 999) {throwIllegalArgumentException(type, 999);} else if (type == IsoType.LLLLBIN && length > 9999) {throwIllegalArgumentException(type, 9999);} else if (type == IsoType.LLBCDBIN && length > 50) {throwIllegalArgumentException(type, 50);} else if (type == IsoType.LLLBCDBIN && length > 500) {throwIllegalArgumentException(type, 500);} else if (type == IsoType.LLLLBCDBIN && length > 5000) {throwIllegalArgumentException(type, 5000);}}private void throwIllegalArgumentException(IsoType t, int maxLength) {throw new IllegalArgumentException(t.name() + " can only hold values up to " + maxLength + " chars");}}

IsoMessage.java

/*
j8583 A Java implementation of the ISO8583 protocol
Copyright (C) 2007 Enrique Zamudio LopezThis library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
*/
package com.swiftplus.posservice.prepose.iso8583;import com.swiftplus.posservice.prepose.iso8583.util.HexCodec;import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.util.BitSet;
import java.util.Map;/** Represents an ISO8583 message. This is the core class of the framework.* Contains the bitmap which is modified as fields are added/removed.* This class makes no assumptions as to what types belong in each field,* nor what fields should each different message type have; that is left* for the developer, since the different ISO8583 implementations can vary* greatly.* * @author Enrique Zamudio*/
public class IsoMessage {static final byte[] HEX = new byte[]{ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };/** The message type. */private int type;private boolean binaryHeader;private boolean binaryFields;/** This is where the values are stored. */@SuppressWarnings("rawtypes")private IsoValue[] fields = new IsoValue[129];/** Stores the optional ISO header. */private String isoHeader;private byte[] binIsoHeader;private int etx = -1;/** Flag to enforce secondary bitmap even if empty. */private boolean forceb2;private boolean binBitmap;private boolean forceStringEncoding;private String encoding = System.getProperty("file.encoding");/** Creates a new empty message with no values set. */public IsoMessage() {}/** Creates a new message with the specified ISO header. This will be prepended to the message. */protected IsoMessage(String header) {isoHeader = header;}/** Creates a new message with the specified binary ISO header. This will be prepended to the message. */protected IsoMessage(byte[] binaryHeader) {binIsoHeader = binaryHeader;}/** Tells the message to encode its bitmap in binary format, even if the message* itself is encoded as text. This has no effect if the binary flag is set, which means* binary messages will always encode their bitmap in binary format. */public void setBinaryBitmap(boolean flag) {binBitmap = flag;}/** Returns true if the message's bitmap is encoded in binary format, when the message* is encoded as text. Default is false. */public boolean isBinaryBitmap() {return binBitmap;}/** If set, this flag will cause the secondary bitmap to be written even if it's not needed. */public void setForceSecondaryBitmap(boolean flag) {forceb2 = flag;}/** Returns true if the secondary bitmap is always included in the message, even* if it's not needed. Default is false. */public boolean getForceSecondaryBitmap() {return forceb2;}/** Sets the encoding to use. */public void setCharacterEncoding(String value) {if (value == null) {throw new IllegalArgumentException("Cannot set null encoding.");}encoding = value;}/** Returns the character encoding for Strings inside the message. Default* is taken from the file.encoding system property. */public String getCharacterEncoding() {return encoding;}/** Specified whether the variable-length fields should encode their length* headers using string conversion with the proper character encoding. Default* is false, which is the old behavior (encoding as ASCII). This is only useful* for text format. */public void setForceStringEncoding(boolean flag) {forceStringEncoding = flag;}/** Sets the string to be sent as ISO header, that is, after the length header but before the message type.* This is useful in case an application needs some custom data in the ISO header of each message (very rare). */public void setIsoHeader(String value) {isoHeader = value;binIsoHeader = null;}/** Returns the ISO header that this message was created with. */public String getIsoHeader() {return isoHeader;}/** Sets the string to be sent as ISO header, that is, after the length header but before the message type.* This is useful in case an application needs some custom data in the ISO header of each message (very rare). */public void setBinaryIsoHeader(byte[] binaryHeader) {isoHeader = null;binIsoHeader = binaryHeader;}/** Returns the binary ISO header that this message was created with. */public byte[] getBinaryIsoHeader() {return binIsoHeader;}/** Sets the ISO message type. Common values are 0x200, 0x210, 0x400, 0x410, 0x800, 0x810. */public void setType(int value) {type = value;}/** Returns the ISO message type. */public int getType() {return type;}/** Indicates whether the message should be binary. Default is false.* To encode the message as text but the bitmap in binary format, you can set the* binaryBitmap flag. */public void setBinary(boolean flag) {binaryHeader = binaryFields = flag;}/** Returns true if the message is binary coded (both header and fields); default is false.* @deprecated Use the new flags isBinaryHeader and isBinaryFields instead.*/@Deprecatedpublic boolean isBinary() {return binaryHeader && binaryFields;}/** header information is binary encoded */public void setBinaryHeader(boolean flag) {binaryHeader = flag;}/** header information is binary encoded */public boolean isBinaryHeader(){return binaryHeader;}/** field data is binary encoded */public void setBinaryFields(boolean flag){binaryFields = flag;}/** field data is binary encoded */public boolean isBinaryFields(){return binaryFields;}/** Sets the ETX character, which is sent at the end of the message as a terminator.* Default is -1, which means no terminator is sent. */public void setEtx(int value) {etx = value;}/** Returns the stored value in the field, without converting or formatting it.* @param field The field number. 1 is the secondary bitmap and is not returned as such;* real fields go from 2 to 128. */public <T> T getObjectValue(int field) {@SuppressWarnings("unchecked")IsoValue<T> v = fields[field];return v == null ? null : v.getValue();}/** Returns the IsoValue for the specified field. First real field is 2. */@SuppressWarnings("unchecked")public <T> IsoValue<T> getField(int field) {return fields[field];}/** Stored the field in the specified index. The first field is the secondary bitmap and has index 1,* so the first valid value for index must be 2.* @return The receiver (useful for setting several fields in sequence). */public IsoMessage setField(int index, IsoValue<?> field) {if (index < 2 || index > 128) {throw new IndexOutOfBoundsException("Field index must be between 2 and 128");}if (field != null) {field.setCharacterEncoding(encoding);}fields[index] = field;return this;}/** Convenience method for setting several fields in one call. */public IsoMessage setFields(Map<Integer, IsoValue<?>> values) {for (Map.Entry<Integer, IsoValue<?>> e : values.entrySet()) {setField(e.getKey(), e.getValue());}return this;}/** Sets the specified value in the specified field, creating an IsoValue internally.* @param index The field number (2 to 128)* @param value The value to be stored.* @param t The ISO type.* @param length The length of the field, used for ALPHA and NUMERIC values only, ignored* with any other type.* @return The receiver (useful for setting several values in sequence). */public IsoMessage setValue(int index, Object value, IsoType t, int length) {return setValue(index, value, null, t, length);}/** Sets the specified value in the specified field, creating an IsoValue internally.* @param index The field number (2 to 128)* @param value The value to be stored.* @param encoder An optional CustomFieldEncoder for the value.* @param t The ISO type.* @param length The length of the field, used for ALPHA and NUMERIC values only, ignored* with any other type.* @return The receiver (useful for setting several values in sequence). */public <T> IsoMessage setValue(int index, T value, CustomFieldEncoder<T> encoder, IsoType t, int length) {if (index < 2 || index > 128) {throw new IndexOutOfBoundsException("Field index must be between 2 and 128");}if (value == null) {fields[index] = null;} else {IsoValue<T> v = null;if (t.needsLength()) {v = new IsoValue<>(t, value, length, encoder,binaryFields);} else {v = new IsoValue<>(t, value, encoder,binaryFields);}v.setCharacterEncoding(encoding);fields[index] = v;}return this;}/** A convenience method to set new values in fields that already contain values.* The field's type, length and custom encoder are taken from the current value.* This method can only be used with fields that have been previously set,* usually from a template in the MessageFactory.* @param index The field's index* @param value The new value to be set in that field.* @return The message itself.* @throws IllegalArgumentException if there is no current field at the specified index. */public <T> IsoMessage updateValue(int index, T value) {IsoValue<T> current = getField(index);if (current == null) {throw new IllegalArgumentException("Value-only field setter can only be used on existing fields");} else {setValue(index, value, current.getEncoder(), current.getType(), current.getLength());getField(index).setCharacterEncoding(current.getCharacterEncoding());getField(index).setTimeZone(current.getTimeZone());}return this;}/** Returns true is the message has a value in the specified field.* @param idx The field number. */public boolean hasField(int idx) {return fields[idx] != null;}/** Writes a message to a stream, after writing the specified number of bytes indicating* the message's length. The message will first be written to an internal memory stream* which will then be dumped into the specified stream. This method flushes the stream* after the write. There are at most three write operations to the stream: one for the* length header, one for the message, and the last one with for the ETX.* @param outs The stream to write the message to.* @param lengthBytes The size of the message length header. Valid ranges are 0 to 4.* @throws IllegalArgumentException if the specified length header is more than 4 bytes.* @throws IOException if there is a problem writing to the stream. */public void write(OutputStream outs, int lengthBytes) throws IOException {if (lengthBytes > 4) {throw new IllegalArgumentException("The length header can have at most 4 bytes");}byte[] data = writeData();if (lengthBytes > 0) {int l = data.length;if (etx > -1) {l++;}byte[] buf = new byte[lengthBytes];int pos = 0;if (lengthBytes == 4) {buf[0] = (byte)((l & 0xff000000) >> 24);pos++;}if (lengthBytes > 2) {buf[pos] = (byte)((l & 0xff0000) >> 16);pos++;}if (lengthBytes > 1) {buf[pos] = (byte)((l & 0xff00) >> 8);pos++;}buf[pos] = (byte)(l & 0xff);outs.write(buf);}outs.write(data);//ETXif (etx > -1) {outs.write(etx);}outs.flush();}/** Creates and returns a ByteBuffer with the data of the message, including the length header.* The returned buffer is already flipped, so it is ready to be written to a Channel. */public ByteBuffer writeToBuffer(int lengthBytes) {if (lengthBytes > 4) {throw new IllegalArgumentException("The length header can have at most 4 bytes");}byte[] data = writeData();ByteBuffer buf = ByteBuffer.allocate(lengthBytes + data.length + (etx > -1 ? 1 : 0));if (lengthBytes > 0) {int l = data.length;if (etx > -1) {l++;}if (lengthBytes == 4) {buf.put((byte)((l & 0xff000000) >> 24));}if (lengthBytes > 2) {buf.put((byte)((l & 0xff0000) >> 16));}if (lengthBytes > 1) {buf.put((byte)((l & 0xff00) >> 8));}buf.put((byte)(l & 0xff));}buf.put(data);//ETXif (etx > -1) {buf.put((byte)etx);}buf.flip();return buf;}/** Creates a BitSet for the bitmap. */protected BitSet createBitmapBitSet() {BitSet bs = new BitSet(forceb2 ? 128 : 64);for (int i = 2 ; i < 129; i++) {if (fields[i] != null) {bs.set(i - 1);}}if (forceb2) {bs.set(0);} else if (bs.length() > 64) {//Extend to 128 if neededBitSet b2 = new BitSet(128);b2.or(bs);bs = b2;bs.set(0);}return bs;}/** Writes the message to a memory stream and returns a byte array with the result. */public byte[] writeData() {ByteArrayOutputStream bout = new ByteArrayOutputStream();if (isoHeader != null) {try {bout.write(isoHeader.getBytes(encoding));} catch (IOException ex) {//should never happen, writing to a ByteArrayOutputStream}} else if (binIsoHeader != null) {try {bout.write(binIsoHeader);} catch (IOException ex) {//should never happen, writing to a ByteArrayOutputStream}}//Message Typeif (binaryHeader) {bout.write((type & 0xff00) >> 8);bout.write(type & 0xff);} else {try {bout.write(String.format("%04x", type).getBytes(encoding));} catch (IOException ex) {//should never happen, writing to a ByteArrayOutputStream}}//BitmapBitSet bs = createBitmapBitSet();//Write bitmap to streamif (binaryHeader || binBitmap) {int pos = 128;int b = 0;for (int i = 0; i < bs.size(); i++) {if (bs.get(i)) {b |= pos;}pos >>= 1;if (pos == 0) {bout.write(b);pos = 128;b = 0;}}} else {ByteArrayOutputStream bout2 = null;if (forceStringEncoding) {bout2 = bout;bout = new ByteArrayOutputStream();}int pos = 0;int lim = bs.size() / 4;for (int i = 0; i < lim; i++) {int nibble = 0;if (bs.get(pos++))nibble |= 8;if (bs.get(pos++))nibble |= 4;if (bs.get(pos++))nibble |= 2;if (bs.get(pos++))nibble |= 1;bout.write(HEX[nibble]);}if (forceStringEncoding) {final String _hb = new String(bout.toByteArray());bout = bout2;try {bout.write(_hb.getBytes(encoding));} catch (IOException ignore) {//never happen}}}//Fieldsfor (int i = 2; i < 129; i++) {IsoValue<?> v = fields[i];if (v != null) {try {v.write(bout, binaryFields, forceStringEncoding);} catch (IOException ex) {//should never happen, writing to a ByteArrayOutputStream}}}return bout.toByteArray();}/** Returns a string representation of the message, as if it were encoded* in ASCII with no binary bitmap. */public String debugString() {StringBuilder sb = new StringBuilder();if (isoHeader != null) {sb.append(isoHeader);} else if (binIsoHeader != null) {sb.append("[0x").append(HexCodec.hexEncode(binIsoHeader, 0, binIsoHeader.length)).append("]");}sb.append(String.format("%04x", type));//BitmapBitSet bs = createBitmapBitSet();int pos = 0;int lim = bs.size() / 4;for (int i = 0; i < lim; i++) {int nibble = 0;if (bs.get(pos++))nibble |= 8;if (bs.get(pos++))nibble |= 4;if (bs.get(pos++))nibble |= 2;if (bs.get(pos++))nibble |= 1;sb.append(new String(HEX, nibble, 1));}//Fieldsfor (int i = 2; i < 129; i++) {IsoValue<?> v = fields[i];if (v != null) {String desc = v.toString();if (v.getType() == IsoType.LLBIN || v.getType() == IsoType.LLBCDBIN || v.getType() == IsoType.LLVAR) {sb.append(String.format("%02d", desc.length()));} else if (v.getType() == IsoType.LLLBIN || v.getType() == IsoType.LLLBCDBIN || v.getType() == IsoType.LLLVAR) {sb.append(String.format("%03d", desc.length()));} else if (v.getType() == IsoType.LLLLBIN || v.getType() == IsoType.LLLLBCDBIN || v.getType() == IsoType.LLLLVAR) {sb.append(String.format("%04d", desc.length()));}sb.append(desc);}}return sb.toString();}//These are for Groovy compat/** Sets the specified value in the specified field, just like {@link #setField(int, IsoValue)}. */public <T> void putAt(int i, IsoValue<T> v) {setField(i, v);}/** Returns the IsoValue in the specified field, just like {@link #getField(int)}. */public <T> IsoValue<T> getAt(int i) {return getField(i);}//These are for Scala compat/** Sets the specified value in the specified field, just like {@link #setField(int, IsoValue)}. */public <T> void update(int i, IsoValue<T> v) {setField(i, v);}/** Returns the IsoValue in the specified field, just like {@link #getField(int)}. */public <T> IsoValue<T> apply(int i) {return getField(i);}/** Copies the specified fields from the other message into the recipient. If a specified field is* not present in the source message it is simply ignored. */public void copyFieldsFrom(IsoMessage src, int...idx) {for (int i : idx) {IsoValue<Object> v = src.getField(i);if (v != null) {setValue(i, v.getValue(), v.getEncoder(), v.getType(), v.getLength());}}}/** Remove the specified fields from the message. */public void removeFields(int... idx) {for (int i : idx) {setField(i, null);}}/** Returns true is the message contains all the specified fields.* A convenience for m.hasField(x) &amp;&amp; m.hasField(y) &amp;&amp; m.hasField(z) &amp;&amp; ... */public boolean hasEveryField(int... idx) {for (int i : idx) {if (!hasField(i)) {return false;}}return true;}/** Returns true is the message contains at least one of the specified fields.* A convenience for m.hasField(x) || m.hasField(y) || m.hasField(z) || ... */public boolean hasAnyField(int... idx) {for (int i : idx) {if (hasField(i)) {return true;}}return false;}
}

测试

        isoMessage.setValue(62,"00062",IsoType.LLLVAR,0);CompositeField compositeField63 = new CompositeField();compositeField63.addValue(new IsoValue(IsoType.ALPHA,"123",3));CompositeField compositeField630 = new CompositeField();compositeField630.addValue(new IsoValue(IsoType.LLVAR,"CARD",0,true));compositeField630.addValue(new IsoValue(IsoType.LLVAR,"UPI",0,true));compositeField630.addValue(new IsoValue(IsoType.LLVAR,"ACCEPT",0,true));compositeField630.addValue(new IsoValue(IsoType.LLVAR,"POS",0,true));compositeField63.addValue(new IsoValue(IsoType.LLLVAR,compositeField630,0,compositeField630,true));isoMessage.setValue(63,compositeField63,compositeField63,IsoType.LLLVAR,0);

请求结果:
在这里插入图片描述

另提供对应解析配置的xml写法:

		<field num="63" type="LLLVAR" ><field num="1" type="ALPHA" length="3" /><field num="2" type="LLLVAR"  ><field num="1" type="LLVAR"  /><field num="2" type="LLVAR"  /><field num="3" type="LLVAR" /><field num="4" type="LLVAR"  /></field></field>

这篇关于J8583包组合字段处理优化的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/298556

相关文章

如何使用celery进行异步处理和定时任务(django)

《如何使用celery进行异步处理和定时任务(django)》文章介绍了Celery的基本概念、安装方法、如何使用Celery进行异步任务处理以及如何设置定时任务,通过Celery,可以在Web应用中... 目录一、celery的作用二、安装celery三、使用celery 异步执行任务四、使用celery

SpringBoot操作spark处理hdfs文件的操作方法

《SpringBoot操作spark处理hdfs文件的操作方法》本文介绍了如何使用SpringBoot操作Spark处理HDFS文件,包括导入依赖、配置Spark信息、编写Controller和Ser... 目录SpringBoot操作spark处理hdfs文件1、导入依赖2、配置spark信息3、cont

正则表达式高级应用与性能优化记录

《正则表达式高级应用与性能优化记录》本文介绍了正则表达式的高级应用和性能优化技巧,包括文本拆分、合并、XML/HTML解析、数据分析、以及性能优化方法,通过这些技巧,可以更高效地利用正则表达式进行复杂... 目录第6章:正则表达式的高级应用6.1 模式匹配与文本处理6.1.1 文本拆分6.1.2 文本合并6

MyBatis延迟加载的处理方案

《MyBatis延迟加载的处理方案》MyBatis支持延迟加载(LazyLoading),允许在需要数据时才从数据库加载,而不是在查询结果第一次返回时就立即加载所有数据,延迟加载的核心思想是,将关联对... 目录MyBATis如何处理延迟加载?延迟加载的原理1. 开启延迟加载2. 延迟加载的配置2.1 使用

Android WebView的加载超时处理方案

《AndroidWebView的加载超时处理方案》在Android开发中,WebView是一个常用的组件,用于在应用中嵌入网页,然而,当网络状况不佳或页面加载过慢时,用户可能会遇到加载超时的问题,本... 目录引言一、WebView加载超时的原因二、加载超时处理方案1. 使用Handler和Timer进行超

Python中处理NaN值的技巧分享

《Python中处理NaN值的技巧分享》在数据科学和数据分析领域,NaN(NotaNumber)是一个常见的概念,它表示一个缺失或未定义的数值,在Python中,尤其是在使用pandas库处理数据时,... 目录NaN 值的来源和影响使用 pandas 的 isna()和 isnull()函数直接比较 Na

详解Python中通用工具类与异常处理

《详解Python中通用工具类与异常处理》在Python开发中,编写可重用的工具类和通用的异常处理机制是提高代码质量和开发效率的关键,本文将介绍如何将特定的异常类改写为更通用的ValidationEx... 目录1. 通用异常类:ValidationException2. 通用工具类:Utils3. 示例文

Vue3 的 shallowRef 和 shallowReactive:优化性能

大家对 Vue3 的 ref 和 reactive 都很熟悉,那么对 shallowRef 和 shallowReactive 是否了解呢? 在编程和数据结构中,“shallow”(浅层)通常指对数据结构的最外层进行操作,而不递归地处理其内部或嵌套的数据。这种处理方式关注的是数据结构的第一层属性或元素,而忽略更深层次的嵌套内容。 1. 浅层与深层的对比 1.1 浅层(Shallow) 定义

无人叉车3d激光slam多房间建图定位异常处理方案-墙体画线地图切分方案

墙体画线地图切分方案 针对问题:墙体两侧特征混淆误匹配,导致建图和定位偏差,表现为过门跳变、外月台走歪等 ·解决思路:预期的根治方案IGICP需要较长时间完成上线,先使用切分地图的工程化方案,即墙体两侧切分为不同地图,在某一侧只使用该侧地图进行定位 方案思路 切分原理:切分地图基于关键帧位置,而非点云。 理论基础:光照是直线的,一帧点云必定只能照射到墙的一侧,无法同时照到两侧实践考虑:关

HDFS—存储优化(纠删码)

纠删码原理 HDFS 默认情况下,一个文件有3个副本,这样提高了数据的可靠性,但也带来了2倍的冗余开销。 Hadoop3.x 引入了纠删码,采用计算的方式,可以节省约50%左右的存储空间。 此种方式节约了空间,但是会增加 cpu 的计算。 纠删码策略是给具体一个路径设置。所有往此路径下存储的文件,都会执行此策略。 默认只开启对 RS-6-3-1024k