使用XmlDocument从带或不带命名空间的xml文件中读取

时间:2015-05-16 18:31:23

标签: c# xml namespaces

我有一些代码使用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>

4 个答案:

答案 0 :(得分:12)

您可以考虑以下选项:

  1. 确定文档是否包含命名空间,并根据它构造xpath查询
  2. 使用名称空间不可知的xpath,例如local-name() 忽略命名空间
  3. 选项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;
         }
    }
    

答案 1 :(得分:1)

使用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元素的命名空间来确定自己的命名空间。

答案 2 :(得分:1)

改进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 :(得分:0)

您可以在 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