Genexus 中如何使用带 namespace 前缀的 XML 填充 SDT?
结论
处理 XML 时,应按“命名空间 URI + 本地名称”识别元素,无需逐次识别并删除变化的 a:、ns1: 等前缀。
先检查 SDT 与 XML 各层元素的命名空间是否一致,再检查当前 GeneXus 版本和生成器的反序列化行为。只设置根级 Exposed namespace,不能保证所有成员都能匹配。即使 FromXml() 返回 true,也要检查数据是否正确填入。
GeneXus 公开的 FromXml() 方法签名中没有“忽略命名空间前缀”的参数,不应添加未经支持的选项。FromXml 方法文档
为什么改成默认 namespace 后就能读取?
前缀是命名空间的别名。只要绑定的 URI 相同,a:FatturaElettronica 和 b:FatturaElettronica 就具有相同的展开名称。
不过,把 xmlns:a="..." 改成 xmlns="...",可能同时改变子元素所属的命名空间。例如,文件中有一个无前缀的子元素:
<a:FatturaElettronica xmlns:a="urn:invoice">
<FatturaElettronicaHeader />
</a:FatturaElettronica>
这里的根元素属于 urn:invoice,子元素没有命名空间。如果改成:
<FatturaElettronica xmlns="urn:invoice">
<FatturaElettronicaHeader />
</FatturaElettronica>
子元素也属于 urn:invoice,两份 XML 的含义便不同了。默认命名空间会作用于其范围内的无前缀元素,但不会自动作用于 versione 这样的无前缀属性。XML 命名空间规范
改成默认 namespace 后能够填充数据,可能是因为子元素的命名空间与 SDT 的匹配情况发生了变化。仅凭根节点片段,还不能认定 GeneXus 无法处理前缀。
先检查 SDT 的 XML 映射
- 核对根元素名称是否为
FatturaElettronica,命名空间 URI 是否完整一致。题目中的http://ivaservizi.agenzia...1.2是缩写,实际配置必须使用文件中的完整值。 - 检查子元素及嵌套结构各自的命名空间,尤其注意无前缀元素和局部的
xmlns=""。 - 核对成员的
XML Name、XML Namespace、XML Type以及集合层次。若要读取versione="FPR12",对应成员应映射为属性。 - 修改
Exposed namespace后执行Rebuild All,确保生成代码已更新。这是设计时属性,官方文档明确要求重建。Exposed namespace 文档
成员的 XML Namespace 与 XML Name 有联动规则。官方文档说明,设置了 XML Name 却未设置 XML Namespace 时,会使用空命名空间。因此,不要假设根级配置会自动覆盖每个成员。可以给测试 SDT 填入已知值,再调用 ToXml(),将输出与输入 XML 的结构和命名空间逐项比较。XML Namespace 文档
为 FromXml 增加诊断信息
FromXml() 支持可选的 Messages 参数。将 &Messages 定义为 GeneXus.Common.Messages,将 &Message 定义为对应的消息项类型:
&text = &file.ReadAllText()
// 使用新实例,避免旧数据干扰判断
&Fattura = new()
&Messages.Clear()
&isOk = &Fattura.FromXml(&text, &Messages)
For &Message in &Messages
msg(&Message.Description)
EndFor
If not &isOk
msg("XML 反序列化返回失败")
EndIf
官方文档对返回值的定义是“发生错误时返回 false,否则返回 true”,同时要求 XML 格式与 SDT 结构兼容。文档没有承诺返回 true 就表示所有输入节点都已映射到成员。FromXml 方法文档
即使返回 true 且没有消息,也要检查预期字段和集合,例如必填标识是否存在、明细数量是否与输入一致。仅凭题目中的现象,无法判断内部具体跳过了哪个节点。
如何自动处理变化的前缀?
可以用 XMLReader 的 LocalName 和 NameSpaceURI 检查节点,避免依赖固定前缀。XMLReader 文档
以下代码会显示每个元素的实际名称和命名空间。&Reader 为 XMLReader,&ReadResult 为 Numeric:
&Reader.OpenFromString(&text)
&ReadResult = &Reader.Read()
Do While &ReadResult > 0
If &Reader.NodeType = 1
msg(
&Reader.LocalName
+ " | namespace="
+ &Reader.NameSpaceURI
)
EndIf
&ReadResult = &Reader.Read()
EndDo
If &Reader.ErrCode <> 0
msg(&Reader.ErrDescription)
EndIf
&Reader.Close()
根元素的匹配条件可以写成:
If &Reader.LocalName = "FatturaElettronica" and
&Reader.NameSpaceURI = &ExpectedNamespace
// 按实际结构读取属性、子元素并赋值给 SDT
EndIf
&ExpectedNamespace 保存完整的预期 URI。无论输入使用 a: 还是其他前缀,这个条件都不需要改变。手动映射嵌套元素时,还应跟踪父子路径,避免混淆不同位置的同名元素。
确认 SDT 映射正确后,如果当前环境的 FromXml() 仍只接受某种前缀写法,可以用 XMLReader 显式映射,也可以通过支持命名空间的 XML 库生成兼容的中间 XML,再调用 FromXml()。转换时必须保留元素和属性原有的命名空间含义。
注意事项
不要用全局字符串替换或正则表达式删除前缀,也不要直接把所有 xmlns:前缀 改成 xmlns。这些操作可能改变子元素的含义,或把不同命名空间中的同名元素混为一谈。
还要核对文件的实际编码。ReadAllText() 已经完成了从字节到字符串的解码,XML 声明中的 ISO-8859-15 无法补救此前的错误解码。
可以构造一组仅改变前缀、保持所有元素命名空间不变的最小样例。如果读取结果仍然不同,再结合 GeneXus 版本、Upgrade、生成器及 SDT 定义定位兼容问题。现有信息不足以确定具体的版本缺陷或修复补丁。
备注:内容仅供参考。
