c# - .NET 与 COM 中的 XML 复杂命名空间

标签 c# xml namespaces

有一些用于发票的德语 xml 格式,由“Fraunhofer Institute”定义,称为 OpenTrans,这是关于它的 2.1 版。根据定义,此类发票文档的标题必须如下所示,包括多个命名空间:

<?xml version="1.0" encoding="ISO-8859-1" standalone="yes"?>
<INVOICE version="2.1" xmlns="http://www.opentrans.org/XMLSchema/2.1"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.opentrans.org/XMLSchema/2.1 opentrans_2_1.xsd"
    xmlns:bmecat="http://www.bmecat.org/bmecat/2005"
    xmlns:xmime="http://www.w3.org/2005/05/xmlmime"/>

我的第一个版本 - 仍在使用 - 已经在 Dynamics Nav classic 中实现,它不直接支持 .Net。因此,这些天我不得不使用 COM 对象 MSXML2。

现在我正尝试在 C#/.Net (4.5.1) 中重写它,但其中一个命名空间出现了一些奇怪的问题。虽然上面由 MSXML2 创建的根节点是正确的(特别是 xsi:schemaLocation 命名空间),但我的 .Net 代码的输出不是我想要的:

<?xml version="1.0" encoding="ISO-8859-1" standalone="yes"?>
<INVOICE version="2.1" xmlns="http://www.opentrans.org/XMLSchema/2.1"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   d1p1:schemaLocation="http://www.opentrans.org/XMLSchema/2.1 opentrans_2_1.xsd"
   xmlns:bmecat="http://www.bmecat.org/bmecat/2005"
   xmlns:xmime="http://www.w3.org/2005/05/xmlmime"
   xmlns:d1p1="http://www.opentrans.org/XMLSchema/2.1">
</INVOICE>

xsi:schemaLocation 已转换为默认命名空间 d1p1:schemaLocation 并且 d1p1(当然)已添加到列表中命名空间。

为了能够比较这两种尝试,我使用相同的旧 MSXML2(Microsoft XML,v6.0)将基于 MSXML2 的旧 Navision 代码转换为 C#,并且我得到了相同的 CORRECT 输出,而输出.Net 代码不会创建我需要获取的命名空间。

这是我的 C# 代码的两个版本:

if (mode == "com")
{
    MSXML2.DOMDocument60 comDoc = new MSXML2.DOMDocument60();
    MSXML2.IXMLDOMProcessingInstruction xmlProcessingInst = comDoc.createProcessingInstruction("xml", "version=\"1.0\" encoding=\"ISO-8859-1\" standalone=\"yes\"");
    comDoc.appendChild(xmlProcessingInst);
    MSXML2.IXMLDOMNode RootNode;
    MSXML2.IXMLDOMElement NewChildNode = comDoc.createElement("INVOICE");
    RootNode = comDoc.appendChild(NewChildNode);
    MSXML2.IXMLDOMAttribute XMLNewAttributeNode = RootNode.ownerDocument.createAttribute("version");
    XMLNewAttributeNode.nodeValue = "2.1";
    RootNode.attributes.setNamedItem(XMLNewAttributeNode);
    XMLNewAttributeNode = RootNode.ownerDocument.createAttribute("xmlns");
    XMLNewAttributeNode.nodeValue = "http://www.opentrans.org/XMLSchema/2.1";
    RootNode.attributes.setNamedItem(XMLNewAttributeNode);
    XMLNewAttributeNode = RootNode.ownerDocument.createAttribute("xmlns:xsi");
    XMLNewAttributeNode.nodeValue = "http://www.w3.org/2001/XMLSchema-instance";
    RootNode.attributes.setNamedItem(XMLNewAttributeNode);
    XMLNewAttributeNode = RootNode.ownerDocument.createAttribute("xsi:schemaLocation");
    XMLNewAttributeNode.nodeValue = "http://www.opentrans.org/XMLSchema/2.1 opentrans_2_1.xsd";
    RootNode.attributes.setNamedItem(XMLNewAttributeNode);
    // the same code for "xmlns:bmecat" attribute;
    // the same code for "xmlns:xmime" attribute;
    comDoc.save(@"D:\testInvoice.xml");
}
else
{
    XmlDocument dotNetDoc = new XmlDocument();
    dotNetDoc.LoadXml("<INVOICE></INVOICE>");
    XmlElement root = dotNetDoc.DocumentElement;
    XmlDeclaration xmlDeclaration = dotNetDoc.CreateXmlDeclaration("1.0", "ISO-8859-1", "yes");
    dotNetDoc.InsertBefore(xmlDeclaration, root);
    root.SetAttribute("version", "2.1");
    root.SetAttribute("xmlns", "http://www.opentrans.org/XMLSchema/2.1");
    root.SetAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
    XmlAttribute att;
    att = dotNetDoc.CreateAttribute("xsi", "schemaLocation", "http://www.opentrans.org/XMLSchema/2.1");
    att.Value = "http://www.opentrans.org/XMLSchema/2.1 opentrans_2_1.xsd";
    root.SetAttributeNode(att);
    root.SetAttribute("xmlns:bmecat", "http://www.bmecat.org/bmecat/2005");
    root.SetAttribute("xmlns:xmime", "http://www.w3.org/2005/05/xmlmime");
    dotNetDoc.AppendChild(root);
    File.WriteAllText(@"\\mbps02\Verwaltung\EDI\openTrans\2_1\testInvoice.xml", dotNetDoc.OuterXml);
}

如果我对 xmlns:xsixsi:schemaLocation 使用相同的 URL,命名空间前缀将是正确的,但是创建的文档当然无法通过验证那个。

使用 .Net 从 3.x 到 4.5.1 进行了测试。

谁错了 - COM、.Net 还是我?这是错误还是功能?

最佳答案

不,它不是 .Net 错误或功能。这只是您的代码中的一个问题。让我们看看指定的命名空间。

root.SetAttribute(
    "xmlns:xsi",
    "http://www.w3.org/2001/XMLSchema-instance" // <----
     );
XmlAttribute att;
att = dotNetDoc.CreateAttribute(
    "xsi",
    "schemaLocation",
    "http://www.opentrans.org/XMLSchema/2.1" // <----
    );

你现在看出区别了吗?因此,您应该重写属性创建并提供 http://www.w3.org/2001/XMLSchema-instance 作为命名空间,因为 xsi 已映射到它:

att = dotNetDoc.CreateAttribute(
    "xsi",
    "schemaLocation",
    "http://www.w3.org/2001/XMLSchema-instance" // <----
    );

注意。如果您使用上面的代码(使用 CreateAttribute 方法),那么您可以省略手动创建 xsi 声明(即 root.SetAttribute("xmlns:xsi", "..."))。该声明将隐式生成。

或者您应该使用下一行代码:

root.SetAttribute(
    "xmlns:xsi",
    "http://www.w3.org/2001/XMLSchema-instance" // <----
     );
root.SetAttribute(
    "schemaLocation",
    "http://www.w3.org/2001/XMLSchema-instance", // <----
    "http://www.opentrans.org/XMLSchema/2.1 opentrans_2_1.xsd"
    );

但这不是您的代码的唯一问题,因为您正在混合解析 XML 以创建根元素和为其他元素创建 DOM。因此,请选择其中之一。

这是最终代码及其 XML 表示形式:

XmlDocument document = new XmlDocument();

XmlDeclaration declaration = document.CreateXmlDeclaration("1.0", "ISO-8859-1", "yes");
document.AppendChild(declaration);

XmlElement invoice = document.CreateElement("INVOICE", "http://www.opentrans.org/XMLSchema/2.1");
document.AppendChild(invoice);

invoice.SetAttribute("version", "2.1");
invoice.SetAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
invoice.SetAttribute("schemaLocation", "http://www.w3.org/2001/XMLSchema-instance", "http://www.opentrans.org/XMLSchema/2.1 opentrans_2_1.xsd");
invoice.SetAttribute("xmlns:bmecat", "http://www.bmecat.org/bmecat/2005");
invoice.SetAttribute("xmlns:xmime", "http://www.w3.org/2005/05/xmlmime");
<?xml version="1.0" encoding="ISO-8859-1" standalone="yes"?>
<INVOICE version="2.1"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://www.opentrans.org/XMLSchema/2.1 opentrans_2_1.xsd"
         xmlns:bmecat="http://www.bmecat.org/bmecat/2005"
         xmlns:xmime="http://www.w3.org/2005/05/xmlmime"
         xmlns="http://www.opentrans.org/XMLSchema/2.1" />

但是,如果您使用的是 .Net 3.5 及更高版本,请使用 LINQ to XML,因为它的可读性更高:

XNamespace xsi = "http://www.w3.org/2001/XMLSchema-instance";

XDocument document = new XDocument(
    new XDeclaration("1.0", "ISO-8859-1", "yes"),
    new XElement(
        XName.Get("INVOICE", "http://www.opentrans.org/XMLSchema/2.1"),
        new XAttribute("version", "2.1"),
        new XAttribute(XNamespace.Xmlns + "xsi", "http://www.w3.org/2001/XMLSchema-instance"),
        new XAttribute(xsi + "schemaLocation", "http://www.opentrans.org/XMLSchema/2.1 opentrans_2_1.xsd"),
        new XAttribute(XNamespace.Xmlns + "bmecat", "http://www.bmecat.org/bmecat/2005"),
        new XAttribute(XNamespace.Xmlns + "xmime", "http://www.w3.org/2005/05/xmlmime")
        )
    );

关于c# - .NET 与 COM 中的 XML 复杂命名空间,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34471463/

相关文章:

java - 2x2 按钮网格布局

c++ - 源文件中的未命名命名空间和局部变量

asp.net - 使用默认命名空间的 HttpHandler

c# - asp.net 中的文件 uploader 不显示文件

c# - 想从 mysql 表中添加一个带有 gridview(行)总和的数据,但它显示 - 在 vs05,c# 中使用未分配的局部变量 'a' 错误?

java - 在 Java 中将损坏的字符转换为正确的值

html - CDATA 应该是 URL 编码的吗?

PHP 自动加载 - 找到文件 - 类 fatal error

c# - 当我的 IIS 工作人员在 ASP.NET 中保存文件时,为什么权限不稳定?

C# FTP上传和下载