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

时间:2015-12-26 12:46:58

标签: c# xml namespaces

有一些德语xml格式的发票,由#34;弗劳恩霍夫研究所定义"称为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:schemaLocationd1p1(当然)已添加到命名空间列表中。

为了能够比较两次尝试,我将基于MSXML2的旧Navision代码转换为C#,使用相同的旧MSXML2(Microsoft XML,v6.0),并获得相同的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的相同网址,则名称空间前缀将是正确的,但当然无法使用该网址验证创建的文档。

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

谁错了 - COM,.Net还是我?这是一个错误还是一个功能?

2 个答案:

答案 0 :(得分:2)

不,它不是.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声明(即{{1} })。该声明将隐式生成。

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

root.SetAttribute("xmlns:xsi", "...")

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

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

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"
    );
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");

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

<?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" />

答案 1 :(得分:-2)

这样做很简单。只需解析字符串xml

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;

namespace ConsoleApplication62
{
    class Program
    {
        static void Main(string[] args)
        {
            string xml =
               "<?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>";

            XDocument doc = XDocument.Parse(xml);

        }
    }
}