Delphi,SOAP和用CDATA包装值
Delphi中使用SOAP进行数据传输时,有时候需要在XML中包裹一些特殊字符的值。然而,直接将这些值作为字符串字面量插入XML中是不可取的,因为这样做可能会导致数据在未来的某个时候出现错误,特别是当数据本身包含需要转义的字符时。
为了解决这个问题,可以使用Delphi中的TDomCDATASection类。这个类是对XML DOM中的CDATASection的Delphi封装。通过使用TDomCDATASection类,可以将需要包裹的值存储为CDATA。
在使用TDomCDATASection类存储base64编码数据的示例可以在这里找到。
需要注意的是,为了进行正确的base64编码,需要协商字符串数据的字符集和原始字节格式(大端、小端、8位、16位等)。
另外,在SOAP响应中插入CDATA需要进行一些底层操作。有几种方法可以尝试:
1. 创建TOPToSoapDomConvert的子类
2. 重写TOPToSoapDomConvert.ConvertNativeDataToSoap或TOPToSoapDomConvert.MakeResponse方法
3. 将TOPToSoapDomConvert的实例赋值给THTTPRIO的Converter属性
或者可以尝试以下方法:
1. 创建TTypeTranslator的子类
2. 重写TTypeTranslator.CastNativeToSoap方法
3. 将TTypeTranslator的实例赋值给TypeTrans单元中的TypeTranslator变量
需要注意的是,这种方法比较困难,如果不需要的话最好不要这样做。
在使用wsdl导入接口并调用接口方法时,会传递生成的类的实例,而不会直接操作XML。从代码中可以看出,THTTPRio类会将方法调用和参数转换为XML并发送到服务器。由于我们不直接操作XML,所以无法看到应该如何使用TDomCDATASection类。
关于为什么要使用CDATA存储已经进行了base64编码的数据,有人提出了不同的观点。其中一种观点认为,如果字符串已经进行了base64编码,那么它不包含任何需要在XML中转义的XML字符,因此没有必要将其放入CDATA节中,而可以直接使用普通的文本节点。
总结起来,如果字符串已经进行了base64编码,那么可以直接使用普通的文本节点。如果字符串包含了需要转义的字符,可以使用Delphi中的TDomCDATASection类将其包裹起来。
问题的原因是供应商可能不理解Cdata的含义,他们可能认为将值放在Cdata节中更容易阅读和编写,但实际上XML解析器并不真正关心这一点。如果是这种情况,可以忽略供应商的指示,继续使用XML库创建普通文本节点,库的序列化程序会自动转义需要转义的字符。
解决方法是告诉供应商他们的系统有问题,如果它不能正确处理Cdata,那么还会有其他什么地方出错呢?除非供应商非常负责,否则你可能没有办法解决这个问题。对于Delphi SOAP,你无法控制XML的生成方式,因为你无法提供IDomDocument,也无法在其上调用createCdataSection来控制程序发送的请求的结构。
文章整理如下:
Cdata、Delphi和在Cdata中包装值的解决方法
Cdata节只是不需要常规XML转义的字符值。例如,您可以使用文本<
而不是使用转义的<
。这就是Cdata的全部意思。如果供应商说值必须在Cdata节中,那么可能有两种可能性:
1. 供应商不理解Cdata的含义。在测试期间,他们可能总是将自己的内容放在Cdata节中,因为这样更容易阅读和编写,但他们可能没有意识到XML解析器实际上并不关心这一点。
2. 供应商使用的XML解析器不符合规范,因此对待Cdata节中的值与裸文本节不同。
如果是第一种情况,那么可以忽略供应商的指示,继续使用XML库创建普通文本节点,库的序列化程序将自动转义需要转义的字符。
但如果是第二种情况,那么应该告诉供应商他们的系统有问题。如果系统不能正确处理Cdata,那还可能有其他什么地方出错呢?除非供应商非常负责,否则你可能无法解决这个问题。对于Delphi SOAP,你无法控制XML的生成方式,因为你无法提供IDomDocument,也无法在其上调用createCdataSection来控制程序发送的请求的结构。
供应商仍然要求使用Cdata,但我看不出实际系统是否真的在乎,所以我会选择第一种选项...