xml解析CDATA中遇到&等特殊字符问题

4 篇文章 0 订阅
4 篇文章 0 订阅

比如一个xml的如下:

<formExport>
	<values>
		<column name="名字">
			<value>&lt;![CDATA[abcd\&$<>阿道夫]]&gt;</value>
		</column>
		<column name="名字2">
			<value>&lt;![CDATA[<刘&智&]]&gt;</value>
		</column>
	</values>
</formExport>

解析xml, 会报如下异常:

Caused by: org.xml.sax.SAXParseException; lineNumber: 1; columnNumber: 65; 在实体引用中, 实体名称必须紧跟在 ‘&’ 后面。

Exception in thread “main” org.xml.sax.SAXParseException; lineNumber: 1; columnNumber: 65; The entity name must immediately follow the ‘&’ in the entity reference

原因: &在xml中是非法字符.

同样的, <>也是有意义的字符, 直接解析会报错

Caused by: org.xml.sax.SAXParseException; lineNumber: 1; columnNumber: 59; 元素内容必须由格式正确的字符数据或标记组成。

Exception in thread “main” org.xml.sax.SAXParseException; lineNumber: 1; columnNumber: 59; The content of elements must consist of well-formed character data or markup.

解决方法

转义 (针对CDATA)中的数据

  private static final Pattern CDATA_PAT_2 = Pattern.compile("&lt;(!\\[CDATA\\[.*?]])&gt;");

  private static final String CHAR_DOLLAR = "RDS_CHAR_DOLLAR";
  /**
   * 替换掉非法字符
   * @param xml
   * @return
   */
  private static String replaceInvalidChar(String xml) {
    Matcher matcher = CDATA_PAT_2.matcher(xml);
    StringBuffer sb = new StringBuffer();
    while (matcher.find()){
      String group = matcher.group(1);
      String rep = group.replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;")
              //如果不将$符号替换, 它在replace方法中会报错 Illegal group reference
              .replaceAll("\\$", CHAR_DOLLAR);;
      //replacement.replaceAll("\\$", "RDS_CHAR_DOLLAR");// encode replacement;
      rep = "&lt;"+rep+"&gt;";
      //appendReplacement会丢失单个斜杠,为了解决这个问题需要处理一次
      rep  = rep.replaceAll("\\\\+", "\\\\\\\\");
      matcher.appendReplacement(sb,rep);
    }
    matcher.appendTail(sb);
    xml = sb.toString().replaceAll(CHAR_DOLLAR,"\\$");
    return xml;
  }

这样得到的xml是这样的:

<formExport>
	<values>
		<column name="名字">
			<value>&lt;![CDATA[abcd\&amp;$&lt;&gt;阿道夫]]&gt;</value>
		</column>
		<column name="名字2">
			<value>&lt;![CDATA[&lt;&amp;&amp;]]&gt;</value>
		</column>
	</values>
</formExport>

这样如果还是希望得到的是转义前的数据, 那就在xml解析成功后, 获取字段值的时候再自己手动替换一遍. 如:

  private static final List<Pair<String, String>> TRANSFER_CHAR_PAIRS = new ArrayList<>();
  static {
    TRANSFER_CHAR_PAIRS.add(Pair.of("&amp;","&"));
    TRANSFER_CHAR_PAIRS.add(Pair.of("&lt;","<"));
    TRANSFER_CHAR_PAIRS.add(Pair.of("&gt;",">"));
  }
 //将
 转义过的进行恢复
    for (Pair<String, String> charPair : TRANSFER_CHAR_PAIRS) {
      s = s.replaceAll(charPair.getLeft(), charPair.getRight());
    }
踩坑
  1. 替换方法中, 有两个坑, 一是美元符号在replaceAllappendReplacement中如果美元符号会特殊处理, $没有跟整数会报错, 所以要先将美元符号替换一下,后面在转回来
  2. 如果有反斜杠\, 在经过appendReplacement处理后也没有了, 和上一个一样, 要先处理下, 再转回来
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值