概述
解决Xstream中解析报文出现Cannot parse date的异常问题
问题描述:
系统线上问题是查询平安银行单笔转账结果的定时任务没有按照预期去更新费用划付的状态,下面是查询平安银行处理返回的报文:
00901048000000000000
A001 01 01 01 00901048000000000000 0000001137 4005
00000 02 20180626 144250 c466f02626144147
000000:交易受理成功
000001
00000000000
<?xml version="1.0" encoding="GBK" ?>
<Result>
<OrigThirdVoucher>602018062600000538</OrigThirdVoucher> --
<FrontLogNo>8043431806261711736105</FrontLogNo> --
<CcyCode>RMB</CcyCode> --
<OutAcctBankName>平安银行青岛分行</OutAcctBankName> --
<OutAcctNo>30205413800006</OutAcctNo> --
<InAcctBankName>招商银行招商银行青岛云霄路支行</InAcctBankName> --
<InAcctNo>532906264010588</InAcctNo>--
<InAcctName>青岛国富金融资产交易中心有限公司</InAcctName>--
<TranAmount>1790.00</TranAmount>--
<UnionFlag>0</UnionFlag>--
--<Yhcljg>MA9112:转账失败-CE3714行内处理失败,失败原因:CE3714清分子账户30205413800006余额不足,且不允许透支</Yhcljg>
<Fee>4.00</Fee>--
<CstInnerFlowNo></CstInnerFlowNo>--
<Stt>30</Stt>--
<IsBack>0</IsBack>--
--<BackRem>CE3714行内处理失败,失败原因:CE3714清分子账户30205413800006余额不足,且不允许透支</BackRem>
<SysFlag>Y</SysFlag>--
<TransBsn>4004</TransBsn>--
<submitTime>2018 06 26 14 41 47</submitTime>--
<AccountDate></AccountDate>--
<InAcctBankNode>308452025147</InAcctBankNode>
<hostFlowNo></hostFlowNo>--
<hostErrorCode>000000</hostErrorCode>--
<kType>0</kType>
<dType>0</dType>
<subBatchNo></subBatchNo>
<ProxyPayName></ProxyPayName>--
<ProxyPayAcc></ProxyPayAcc>--
<ProxyPayBankName></ProxyPayBankName>--
</Result>
分析上面的报文,由于程序中没有打印出出错的日志,只是将查询的结果返回为空,定位到返回空的逻辑处,发现只是针对两个自定义异常的进行了捕获,对于其它的异常并没有抛出和记录,如下

具体分析代码后,最后定位在可能是解析报文出错,因为其它逻辑出都有明确的判断和分析结果。

问题重现:
自己试着重新写测试区解析报文,
public class XStreamTest {
@Test
@SuppressWarnings("deprecation")
public void testFromXml() {
XStream xStream = new XStream(new DomDriver("GBK", new XmlFriendlyReplacer("_-", "_")));
xStream.registerConverter(new DateConverter("yyyy-MM-dd HH:mm:ss",
new String[]{"yyyy-MM-dd","HH:mm:ss","yyyyMMdd","HHmmss","yyyyMMdd HHmmss","yyyyMMddHHmmss"},
TimeZone.getTimeZone("Asia/Shanghai")));
xStream.registerConverter(new PabBigDecimalConverter());
xStream.ignoreUnknownElements();
xStream.processAnnotations(PabTradeResponse.class);
xStream.processAnnotations(PabQuerySinglePaymentResponse.class);
xStream.alias("Result", PabTradeResponse.class, PabQuerySinglePaymentResponse.class);
String resXml = "<?xml version="1.0" encoding="GBK" ?>rn" +
"<Result><OrigThirdVoucher>602018062600000538</OrigThirdVoucher>rn" +
"<FrontLogNo>8043431806261711736105</FrontLogNo>rn" +
"<CcyCode>RMB</CcyCode>rn" +
"<OutAcctBankName>平安银行青岛分行</OutAcctBankName>rn" +
"<OutAcctNo>30205413800006</OutAcctNo>rn" +
"<InAcctBankName>招商银行招商银行青岛云霄路支行</InAcctBankName>rn" +
"<InAcctNo>532906264010588</InAcctNo>rn" +
"<InAcctName>青岛国富金融资产交易中心有限公司</InAcctName>rn" +
"<TranAmount>1790.00</TranAmount>rn" +
"<UnionFlag>0</UnionFlag>rn" +
"<Yhcljg>MA9112:转账失败-CE3714行内处理失败,失败原因:CE3714清分子账户30205413800006余额不足,且不允许透支</Yhcljg>rn" +
"<Fee>4.00</Fee>rn" +
"<CstInnerFlowNo></CstInnerFlowNo>rn" +
"<Stt>30</Stt>rn" +
"<IsBack>0</IsBack>rn" +
"<BackRem>CE3714行内处理失败,失败原因:CE3714清分子账户30205413800006余额不足,且不允许透支</BackRem>rn" +
"<SysFlag>Y</SysFlag>rn" +
"<TransBsn>4004</TransBsn>rn" +
"<submitTime>20180626144147</submitTime>rn" +
"<AccountDate></AccountDate>rn" +
"<InAcctBankNode>308452025147</InAcctBankNode>rn" +
"<hostFlowNo></hostFlowNo>rn" +
"<hostErrorCode>000000</hostErrorCode>rn" +
"<kType>0</kType>rn" +
"<dType>0</dType>rn" +
"<subBatchNo></subBatchNo>rn" +
"<ProxyPayName></ProxyPayName>rn" +
"<ProxyPayAcc></ProxyPayAcc>rn" +
"<ProxyPayBankName></ProxyPayBankName>rn" +
"</Result>";
PabTradeResponse tradeResponse = (PabTradeResponse) xStream.fromXML(resXml);
PabQuerySinglePaymentResponse pabQuerySinglePaymentResponse = (PabQuerySinglePaymentResponse)tradeResponse;
System.out.println(pabQuerySinglePaymentResponse.getAccountDate());
}
}
异常信息:
Exception in thread "main" com.thoughtworks.xstream.converters.ConversionException: Cannot parse date
---- Debugging information ----
message : Cannot parse date
date :
class : java.util.Date
required-type : java.util.Date
converter-type : com.thoughtworks.xstream.converters.SingleValueConverterWrapper
wrapped-converter : com.thoughtworks.xstream.converters.basic.DateConverter
Default date pattern: yyyy-MM-dd HH:mm:ss
Default era date pattern: yyyy-MM-dd G HH:mm:ss.S z
Alternative date pattern: yyyy-MM-dd
Alternative date pattern[1]: HH:mm:ss
Alternative date pattern[2]: yyyyMMdd
Alternative date pattern[3]: HHmmss
Alternative date pattern[4]: yyyyMMdd HHmmss
Alternative date pattern[5]: yyyyMMddHHmmss
path : /Result/AccountDate
class[1] : com.huajin.payproxy.paygateway.pabbank.response.PabQuerySinglePaymentResponse
converter-type[1] : com.thoughtworks.xstream.converters.reflection.ReflectionConverter
version : 1.4.9
-------------------------------
at com.thoughtworks.xstream.converters.basic.DateConverter.fromString(DateConverter.java:230)
at com.thoughtworks.xstream.converters.SingleValueConverterWrapper.fromString(SingleValueConverterWrapper.java:41)
at com.thoughtworks.xstream.converters.SingleValueConverterWrapper.unmarshal(SingleValueConverterWrapper.java:49)
at com.thoughtworks.xstream.core.TreeUnmarshaller.convert(TreeUnmarshaller.java:72)
at com.thoughtworks.xstream.core.AbstractReferenceUnmarshaller.convert(AbstractReferenceUnmarshaller.java:70)
at com.thoughtworks.xstream.core.TreeUnmarshaller.convertAnother(TreeUnmarshaller.java:66)
at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.unmarshallField(AbstractReflectionConverter.java:503)
at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.doUnmarshal(AbstractReflectionConverter.java:429)
at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.unmarshal(AbstractReflectionConverter.java:281)
at com.thoughtworks.xstream.core.TreeUnmarshaller.convert(TreeUnmarshaller.java:72)
at com.thoughtworks.xstream.core.AbstractReferenceUnmarshaller.convert(AbstractReferenceUnmarshaller.java:70)
at com.thoughtworks.xstream.core.TreeUnmarshaller.convertAnother(TreeUnmarshaller.java:66)
at com.thoughtworks.xstream.core.TreeUnmarshaller.convertAnother(TreeUnmarshaller.java:50)
at com.thoughtworks.xstream.core.TreeUnmarshaller.start(TreeUnmarshaller.java:134)
at com.thoughtworks.xstream.core.AbstractTreeMarshallingStrategy.unmarshal(AbstractTreeMarshallingStrategy.java:32)
at com.thoughtworks.xstream.XStream.unmarshal(XStream.java:1230)
at com.thoughtworks.xstream.XStream.unmarshal(XStream.java:1214)
at com.thoughtworks.xstream.XStream.fromXML(XStream.java:1085)
at com.thoughtworks.xstream.XStream.fromXML(XStream.java:1076)
at com.huajin.payproxy.xstream.main.XStreamTest.main(XStreamTest.java:74)
异常分析
果然抛出了异常,异常信息说是报文中的<AccountDate></AccountDate>节点为空,传入的字符串为空,看上面的报文的确是银行返回的是空,那么对于空的时间日期Xstream是怎么处理的呢?看源码可知:
public class DateConverter extends AbstractSingleValueConverter implements ErrorReporter {
public Object fromString(String str) {
if (defaultEraFormat != null) {
try {
return defaultEraFormat.parse(str);
} catch (ParseException e) {
// try next ...
}
}
if (defaultEraFormat != defaultFormat) {
try {
return defaultFormat.parse(str);
} catch (ParseException e) {
// try next ...
}
}
for (int i = 0; i < acceptableFormats.length; i++ ) {
try {
return acceptableFormats[i].parse(str);
} catch (ParseException e3) {
// no worries, let's try the next format.
}
}
// no dateFormats left to try
ConversionException exception = new ConversionException("Cannot parse date");
exception.add("date", str);
throw exception;
}
}
这个方法仅仅是封装后的,看了一部分源码后,这里面并没有判断传入的字符串是空的情况,于是自己在报文中<AccountDate></AccountDate>节点上加了一个时间,

运行结果是

这就验证了上面出错的可能性是极大的。
解决思路
既然平安银行返回的报文中并没有这个数值,并且这个数值可能是会用到的,因此并没有在实体类中去掉这个属性字段,依然保留,可以自定义一个时间转换器继承Xstream的时间转换器DomDriver,重写其中的fromString方法即可自定义的时间转换类源码:
import java.util.TimeZone;
import org.apache.commons.lang3.StringUtils;
import com.thoughtworks.xstream.converters.basic.DateConverter;
/**
* 自定义Xstream时间日期转换工具类
*
* @author hongwei.lian
* @date 2018年6月27日 下午2:33:19
*/
public class CustomDateConverter extends DateConverter {
public CustomDateConverter() {};
/**
* 构造方法
*
* @param string
* @param strings
* @param timeZone
*/
public CustomDateConverter(String string, String[] strings, TimeZone timeZone) {
super(string, strings, timeZone);
}
/**
* 时间日期转换
*/
@Override
public Object fromString(String str) {
if (StringUtils.isBlank(str)) {
return null;
}
return super.fromString(str);
}
}
重新测试报文中<AccountDate></AccountDate>节点依然为空,把注册到Xstream中的时间转换器替换为自定义的CustomDateConverter

重新运行结果为:

总结:
由于编写代码时抛出的异常信息不完整,致使花费了几个小时去定位问题,对于以后代码会尽可能去抛出和记录中日志中。
Xstream使用了这么长时间,并没有发现问题,读开源框架的源码还是有必要的,自己也在努力做到阅读框架源码。
最后
以上就是微笑帆布鞋为你收集整理的解决Xstream中解析报文出现Cannot parse date的异常问题的全部内容,希望文章能够帮你解决解决Xstream中解析报文出现Cannot parse date的异常问题所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复