拼接 XML 字符串,特殊字符串导致 XML 解析异常


场景

最近在做 SOAP 协议的 XML 拼接,直接使用了拼接字符串的方式,但是节点的特殊值导致 Web Service 无法正常的解析 XML 字符串

XML 中不能出现的特殊字符

正常来说,只有"<"字符和"&"字符对于XML来说是严格禁止使用的。 需要注意的是:

  • 转义序列各字符间不能有空格;
  • 转义序列必须以";"结束;
  • 单独的&不被认为是转义开始;
  • 区分大小写。

以下是XML中需要的转义字符:

  • &(逻辑与) &
  • <(小于) <
  • >(大于) >
  • "(双引号) "
  • '(单引号) '

解决方法一 进行base64转码

在发送报文之前,针对报文进行base64转码,转义后避免报文中含有特殊字符。

标准base64中是以%开头的,如果存储到数据库中,并进行查询sql中,会解析成通配符,这样会报错。

搜索后使用改进版base64进行转码,可以规避该问题,具体度娘一下吧。

此方法的缺点是,所有客户端都需要知道解码的方式,否则无法解析。

解决方法二 使用cdata标记

在xml中,使用cdata标记特殊字符,这种方式经分析后不太好。

目前的缺点有2,一是需要针对每个特殊字符都转换一下,首先是本人不知道应该在什么使用添加<![CDATA[ ]]>,是在设置属性值的时候,还是最后生成报文的时候;

而且使用这个的话数据中不能含有[[ ]]等字符。

解决方法三 过滤特殊字符

private string EncodeChar(string source)
{
    if (string.IsNullOrEmpty(source))
    {
        return "";
    }

    StringBuilder sb = new StringBuilder();
    source.ToList().ForEach(_ =>
    {
        switch (_.ToString())
        {
            case "&":
                sb.Append("&");
                break;
            case "<":
                sb.Append("<");
                break;
            case ">":
                sb.Append(">");
                break;
            case "\"":
                sb.Append(""");
                break;
            case "\'":
                sb.Append("'");
                break;
            default:
                sb.Append(_.ToString());
                break;
        }
    });

    return sb.ToString(); ;
}

目前最完美的办法 LINQ to XML

生成 XML

XNamespace soapenv = "http://schemas.xmlsoap.org/soap/envelope/";
    var document = new XDocument(
        new XDeclaration("1.0", "utf-8", String.Empty),
        new XElement(soapenv + "Envelope",
            new XAttribute(XNamespace.Xmlns + "soapenv", soapenv),
            new XElement(soapenv + "Header",
                new XElement(soapenv + "AnyOptionalHeader",
                    new XAttribute("AnyOptionalAttribute", "false")
                )
            ),
            new XElement(soapenv + "Body",
                new XElement(soapenv + "MyMethodName",
                    new XAttribute("AnyAttributeOrElement", "Whatever")
                )
            )
        )
    );

解析服务器返回的 XML

<?xml version="1.0" encoding="utf-8" ?>
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <env:Header>
    <NotifySOAPHeader xmlns="http://www.csapi.org/schema/parlayx/common/v2_0">
      <ServiceID>3001400002</ServiceID>
      <SPrevID>fanxiang123</SPrevID>
      <SPrevPassword>12345678</SPrevPassword>
      <TransactionID>01022016400400000025</TransactionID>
    </NotifySOAPHeader>
  </env:Header>
  <env:Body>
    <notifySmsReception xmlns="http://www.csapi.org/schema/parlayx/sms/notification/v2_0/local">
      <registrationIdentifier/>
      <message>
        <message>aaaa</message>
        <senderAddress>tel:+861067453262</senderAddress>
        <smsServiceActivationNumber>tel:10661112</smsServiceActivationNumber>
      </message>
    </notifySmsReception>
  </env:Body>
</env:Envelope>


XmlDocument xdoc = new XmlDocument();
xdoc.Load("Data.xml");

string s = xdoc.DocumentElement["env:Header"]["NotifySOAPHeader"].InnerXml;
Console.WriteLine(s);

小结

第四种方法写起来比拼接字符串复杂,但是.NET 类库可以很好的帮助我们完成特殊字符的处理,同时,对于服务器的解析,也很简单。

知识共享许可协议
《拼接 XML 字符串,特殊字符串导致 XML 解析异常》 常伟华 创作。
本作品采用知识共享署名-相同方式共享 4.0 国际许可协议 | 3.0 中国大陆许可协议进行许可。

站内公告

A PHP Error was encountered

Severity: Core Warning

Message: PHP Startup: zip: Unable to initialize module Module compiled with module API=20060613 PHP compiled with module API=20090626 These options need to match

Filename: Unknown

Line Number: 0

Backtrace: