C#使用多个变量名称空间读取XML

时间:2016-11-03 08:38:27

标签: c# xml xpath namespaces

我必须从具有已定义结构的XML中读取一些标记和属性,但由于这些文件可以从不同的源生成,因此它们可以具有不同的名称空间和前缀。

这是第一个XML示例

<Order xmlns="urn:oasis:names:specification:ubl:schema:xsd:Order-2" xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2" xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2" xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <cbc:UBLVersionID>2.1</cbc:UBLVersionID>
    <cbc:CustomizationID>urn:www.cenbii.eu:transaction:biitrns001:ver2.0:extended:urn:www.peppol.eu:bis:peppol3a:ver2.0:extended:urn:www.ubl-italia.org:spec:ordine:ver2.1</cbc:CustomizationID>
    <cbc:ID>ORD-001</cbc:ID>
    <cbc:IssueDate>2016-10-01</cbc:IssueDate>
    <cbc:OrderTypeCode listID="UNCL1001">221</cbc:OrderTypeCode>
    <cac:ValidityPeriod>
        <cbc:EndDate>2024-10-19</cbc:EndDate>
    </cac:ValidityPeriod>
    <cac:BuyerCustomerParty>
        <cac:Party>
            <cbc:EndpointID schemeID="IT:IPA">ITAK12MH</cbc:EndpointID>
            <cac:PartyIdentification>
                <cbc:ID schemeID="IT:VAT">01567570254</cbc:ID>
            </cac:PartyIdentification>
            <cac:PartyName>
                <cbc:Name>A Custom Name</cbc:Name>
            </cac:PartyName>
        </cac:Party>
    </cac:BuyerCustomerParty>
</Order>

这是第二个具有不同命名空间和前缀的XML示例,但结构(标记,属性)相同。

<ns10:Order xmlns="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2" xmlns:ns2="urn:oasis:names:specification:ubl:schema:xsd:CommonExtensionComponents-2" xmlns:ns3="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2" xmlns:ns4="http://www.w3.org/2000/09/xmldsig#" xmlns:ns5="http://uri.etsi.org/01903/v1.3.2#" xmlns:ns6="urn:oasis:names:specification:ubl:schema:xsd:SignatureBasicComponents-2" xmlns:ns7="urn:oasis:names:specification:ubl:schema:xsd:SignatureAggregateComponents-2" xmlns:ns8="http://uri.etsi.org/01903/v1.4.1#" xmlns:ns9="urn:oasis:names:specification:ubl:schema:xsd:CommonSignatureComponents-2" xmlns:ns10="urn:oasis:names:specification:ubl:schema:xsd:Order-2">
    <UBLVersionID>2.1</UBLVersionID>
    <CustomizationID>urn:www.cenbii.eu:transaction:biitrns001:ver2.0:extended:urn:www.peppol.eu:bis:peppol3a:ver2.0:extended:urn:www.ubl-italia.org:spec:ordine:ver2.1</CustomizationID>
    <ID>ORD-001</ID>
    <IssueDate>2016-10-01</IssueDate>
    <OrderTypeCode listID="UNCL1001">221</OrderTypeCode>
    <ns3:ValidityPeriod>
        <EndDate>2024-10-19</EndDate>
    </ns3:ValidityPeriod>
    <ns3:BuyerCustomerParty>
        <ns3:Party>
            <EndpointID schemeID="IT:IPA">ITAK12MH</EndpointID>
            <ns3:PartyIdentification>
                <ID schemeID="IT:VAT">01567570254</ID>
            </ns3:PartyIdentification>
            <ns3:PartyName>
                <Name>A Custom Name</Name>
            </ns3:PartyName>
        </ns3:Party>
    </ns3:BuyerCustomerParty>
</ns10:Order>

这些文件必须被认为是相同的,因此都是有效的。

第三个示例可以是类似于第二个示例,其中命名空间相同但前缀不同。显然重要的是用于匹配命名空间的前缀属于该特定标记。

我无法提前知道与命名空间相关的前缀是什么。

<aaa:Order xmlns="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2" xmlns:aaa="urn:oasis:names:specification:ubl:schema:xsd:Order-2" xmlns:bbb="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2">
    <UBLVersionID>2.1</UBLVersionID>
    <CustomizationID>urn:www.cenbii.eu:transaction:biitrns001:ver2.0:extended:urn:www.peppol.eu:bis:peppol3a:ver2.0:extended:urn:www.ubl-italia.org:spec:ordine:ver2.1</CustomizationID>
    <ID>ORD-001</ID>
    <IssueDate>2016-10-01</IssueDate>
    <OrderTypeCode listID="UNCL1001">221</OrderTypeCode>
    <bbb:ValidityPeriod>
        <EndDate>2024-10-19</EndDate>
    </bbb:ValidityPeriod>
    <bbb:BuyerCustomerParty>
        <bbb:Party>
            <EndpointID schemeID="IT:IPA">ITAK12MH</EndpointID>
            <bbb:PartyIdentification>
                <ID schemeID="IT:VAT">01567570254</ID>
            </bbb:PartyIdentification>
            <bbb:PartyName>
                <Name>A Custom Name</Name>
            </bbb:PartyName>
        </bbb:Party>
    </bbb:BuyerCustomerParty>
</aaa:Order>

必须将此最后一个文件视为有效。

如您所见,标记与其命名空间之间的关联始终是相同的。唯一改变的是前缀。

我的实际代码使用XDocument和XElement类来读取XML,但它可能就是这样,因为我需要知道每个标记的确切前缀,因为它们可以变化,所以它只适用于第一个XML文件样本。

XDocument doc;
XmlNamespaceManager manager;

using (XmlReader reader = XmlReader.Create(stream))
{
    doc = XDocument.Load(reader);

    // Retrieving namespaces of XML file
    XPathNavigator navigator = doc.CreateNavigator();
    navigator.MoveToFollowing(XPathNodeType.Element);
    IDictionary<string, string> namespaces = navigator.GetNamespacesInScope(XmlNamespaceScope.All);

    // Add namespaces to an XmlNamespaceManager to read nodes
    manager = new XmlNamespaceManager(reader.NameTable);
    foreach (KeyValuePair<string, string> ns in namespaces)
    {
        manager.AddNamespace(ns.Key, ns.Value);
    }
}

XElement currentNode;

currentNode = doc.Root.XPathSelectElement("cbc:ID", manager);
if (currentNode != null)
    item.DespatchAdviceId = currentNode.Value;

currentNode = doc.Root.XPathSelectElement("cbc:IssueDate", manager);
if (currentNode != null)
{
    DateTime dataEmissione;
    if (DateTime.TryParseExact(currentNode.Value, validDateFormats, CultureInfo.InvariantCulture, DateTimeStyles.None, out dataEmissione))
        item.OrderIssueDate = dataEmissione;
}

currentNode = doc.Root.XPathSelectElement("cac:BuyerCustomerParty/cac:Party/cac:PartyIdentification/cbc:ID", manager);
if (currentNode != null)
{
    item.BuyerPartyId = currentNode.Value;
    if (currentNode.Attribute("schemeID") != null)
        item.BuyerPartySchemeId = currentNode.Attribute("schemeID").Value;
}

// ... and so on...

如何在不指定名称空间前缀的情况下读取XML? 我应该使用另一个.NET库还是第三方?

2 个答案:

答案 0 :(得分:1)

使用LocalName,您可以在不添加命名空间的情况下使用它。

//this is for <cbc:ID>ORD-001</cbc:ID>
var element = doc.Root.Elements().Where(x => x.Name.LocalName == "ID").FirstOrDefault();

如果你想进入嵌套元素

var element = doc.Root.Elements().Where(x => x.Name.LocalName == "ValidityPeriod").
                 Elements().Where(x=> x.Name.LocalName == "EndDate").FirstOrDefault();

答案 1 :(得分:0)

  

我需要知道每个标签的确切前缀。

不,你不是。前缀与元素或属性的限定名称完全无关。如果您想要使用XPath路由,那么请不要从文档中读取命名空间和前缀来创建命名空间管理器,自己指定它们以便了解它们是什么。然后在查询中使用它们。例如,这将适用于任何XML文档:

var manager = new XmlNamespaceManager(new NameTable());

manager.AddNamespace("cbc", 
    "urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2");

var id = doc.Root.XPathSelectElement("cbc:ID", manager);

但是,我鼓励你放弃XPath。 LINQ to XML非常好用。另一个快速提示,有一个XDocument.Load的重载接受一个流。无需创建XmlReader。所以:

XNamespace order = "urn:oasis:names:specification:ubl:schema:xsd:Order-2";
XNamespace cbc = "urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2";
XNamespace cac = "urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2";

var doc = XDocument.Load(stream);

var id = (string) doc.Elements(order + "Order")
    .Elements(cbc + "ID")
    .Single();

var issueDate = (DateTime) doc.Elements(order + "Order")
    .Elements(cbc + "IssueDate")
    .Single();

var buyerPartySchemeId = (string) doc.Descendants(cac + "BuyerCustomerParty")
    .Descendants(cbc + "ID")
    .Attributes("schemeID")
    .Single();