【问题标题】:Read from xml files with or without a namespace using XmlDocument使用 XmlDocument 从有或没有命名空间的 xml 文件中读取
【发布时间】:2015-07-28 13:07:48
【问题描述】:

我有一些代码使用 XmlDocument 从具有命名空间的 xml 文件中读取。我的挑战是我现在正在读取硬编码的文件的命名空间,我将其传递给 XmlNamespaceManager。我想为我的方法更灵活一点。从任何类型的 xml 文件中读取。如果它有命名空间,则使用命名空间管理器来读取元素,而无需对命名空间进行硬编码。如果文件没有命名空间,则去前进,只需解析它。下面是我所做的。

xmldoc = new XmlDocument ();
xmldoc.Load (fileLocation);


XmlNamespaceManager nameSpaceManager = new XmlNamespaceManager(xmldoc.NameTable);

nameSpaceManager.AddNamespace ("ns","http://schemas.sample.data.org/2005");

XmlNodeList nodeList = xmldoc.SelectNodes("/ns:Demo/ns:Items",  nameSpaceManager);
if (nodeList != null) 
{
    foreach (XmlNode childNode in nodeList) 
    {
        string first = childNode.SelectSingleNode ("ns:First", nameSpaceManager).InnerText;
        string second= childNode.SelectSingleNode ("ns:Second", nameSpaceManager).InnerText;
        string third = childNode.SelectSingleNode ("ns:Third", nameSpaceManager).InnerText;
    }
}

这是我正在使用的示例 xml 文件

<Demo xmlns:i="http://www.justasample.com" xmlns="http://schemas.sample.data.org/2005">
 <Items>

  <First>first</First>
  <Second>second</Second>
  <Third>third</Third>

  </Items>

</Demo>

【问题讨论】:

  • 有什么理由不使用 XDocument?这使得一切,尤其是。命名空间,容易得多。
  • 好的,它不是真正的重复。但是,请更清楚地了解您当前的方法适用或无效。
  • @HenkHolterman ,它不一定只是 XmlDocument。我愿意接受任何其他建议。我想我把它放在问题中,因为这是我已经实施的。并澄清我的问题,我目前的方法效果不佳,因为名称空间是硬编码的。我需要摆脱它并使其能够与任何 xml 文件一起使用。

标签: c# xml namespaces


【解决方案1】:

您可以考虑以下选项:

  1. 确定文档是否包含命名空间并根据它构造xpath查询
  2. 使用与命名空间无关的 xpath,例如 local-name(),这将 忽略命名空间

选项 1

var xmlDoc = new XmlDocument();
xmlDoc.Load(fileLocation);
//determine  whether document contains namespace
var namespaceName = "ns";
var namespacePrefix = string.Empty;
XmlNamespaceManager nameSpaceManager = null;
if (xmlDoc.FirstChild.Attributes != null)
{
    var xmlns = xmlDoc.FirstChild.Attributes["xmlns"];
    if (xmlns != null)
    {
          nameSpaceManager = new XmlNamespaceManager(xmlDoc.NameTable);
          nameSpaceManager.AddNamespace(namespaceName, xmlns.Value);
          namespacePrefix = namespaceName + ":";
    }
}

XmlNodeList nodeList = xmlDoc.SelectNodes(string.Format("/{0}Demo/{0}Items",namespacePrefix), nameSpaceManager);
if (nodeList != null)
{
    foreach (XmlNode childNode in nodeList)
    {
       string first = childNode.SelectSingleNode(namespacePrefix + "First", nameSpaceManager).InnerText;
       string second = childNode.SelectSingleNode(namespacePrefix + "Second", nameSpaceManager).InnerText;
       string third = childNode.SelectSingleNode(namespacePrefix +  "Third", nameSpaceManager).InnerText;
     }
 }

选项 2

XmlNodeList nodeList = xmlDoc.SelectNodes("/*[local-name() = 'Demo']/*[local-name() = 'Items']");
if (nodeList != null)
{
    foreach (XmlNode childNode in nodeList)
    {    
        string first = childNode.SelectSingleNode("*[local-name() = 'First']").InnerText;
        string second = childNode.SelectSingleNode("*[local-name() = 'Second']").InnerText;
        string third = childNode.SelectSingleNode("*[local-name() = 'Third']").InnerText;
     }
}

【讨论】:

  • 非常感谢您的回答。我将立即实施,并会告诉您它是如何工作的。
  • 我尝试了这两个选项,它们都工作得很好。非常感谢。我接受了这个作为答案,因为它简单且解释清楚。一个快速的问题,关于实施选项 2,有没有办法像我们对命名空间所做的那样忽略那个节点“Demo”?假设“Demo”是自动生成的并且不是恒定的..下次它将有另一个唯一的名称。但内部节点将始终被称为“ Items"。是否可以忽略实现中的'Demo'但仍然解析文件?
  • 当然,xpath //*[local-name() = 'Items'] 可以解决问题,例如:XmlNodeList nodeList = xmlDoc.SelectNodes("//*[local-name() = 'Items']");
  • 我也实现了最后一部分,它可以工作。这就是我现在拥有的: xmlDoc.SelectNodes("//*[local-name() = 'Items']");
  • 选项2很好,这样,你可以用多个命名空间解析它,效果很好。
【解决方案2】:

改进 Vadim 的 Option1,但使用 XDocument(而不是 XmlDocument)API 和 F# 而不是 C#:

let fileStream = File.Open(fileLocation, FileMode.Open)
let xDocument = XDocument.Load fileStream
let nsOpt =
    let nsString = xDocument.Root.Name.Namespace.ToString()
    if String.IsNullOrEmpty nsString then
        Console.Error.WriteLine "Warning: no namespace URL found in xmlns attrib"
        None
    else
        let nsManager = XmlNamespaceManager(NameTable())
        let nsPrefix = "x"
        nsManager.AddNamespace(nsPrefix, nsString)
        if nsString <> "http://schemas.sample.data.org/2005" then
            Console.Error.WriteLine "Warning: the namespace URL doesn't match expectations, query may result in no elements"
        Some(nsManager, sprintf "%s:" nsPrefix)

let query = "//{0}Demo/{0}Items"
let nodes =
    match nsOpt with
    | None ->
        let fixedQuery = String.Format(query, String.Empty)
        xDocument.XPathSelectElements fixedQuery
    | Some(nsManager, nsPrefix) ->
        let fixedQuery = String.Format(query, nsPrefix)
        xDocument.XPathSelectElements(fixedQuery, nsManager)
for node in nodes do
    ...

【讨论】:

    【解决方案3】:

    使用 Linq-to-XML 和我的library extensions,特别是内部处理的ToXName,你可以这样做:

    XElement root = XElement.Load(fileLocation);
    var items = root.Descendants("Items")
                    .Select(item => new
                    {
                        First = item.Get("First", ""),
                        Second= item.Get("Second", ""),
                        Third = item.Get("Third", "")
                    });
    

    所以每个FirstSecondThird元素都会使用Items元素的命名空间,来确定自己的命名空间。

    【讨论】:

    • @Chuck Savage 感谢您的回复。我很感激。
    【解决方案4】:

    可以在xml文件中给all namespaces动态添加。

    var document = new XmlDocument();
    document.LoadXml(rawData);
    
    var nsmgr = new XmlNamespaceManager(document.NameTable);
    nsmgr.AddNamespace("dpx", document.DocumentElement.NamespaceURI); //default
    
    var attributeCollection = document.DocumentElement.Attributes;
    for (int i = 0; i < attributeCollection.Count; i++)
    {
        var isAttribute = nsmgr.LookupNamespace(attributeCollection[i].LocalName) == null;     
        if (isAttribute)
            nsmgr.AddNamespace(attributeCollection[i].LocalName, attributeCollection[i].Value);
    }
    
    XmlElement xmlElem = document.DocumentElement;
    var node = xmlElem.SelectSingleNode(xpath, nsmgr);
    

    如果 XML 中的节点没有prefix,则在给出路径时应该给它们default prefix

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-03-17
      相关资源
      最近更新 更多