简体   繁体   中英

Unable to get values from XML using xPath C#

I have two XML files, I am able to get data through xPath in c# from first XML which here:

<CONSOLIDATED_LIST xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
                    xsi:noNamespaceSchemaLocation="https://www.example/acb.xsd" 
                    dateGenerated="2018-04-19T19:18:41.129-04:00">
  <INDIVIDUALS>
    <INDIVIDUAL>
      <DATAID>2975591</DATAID>
      <VERSIONNUM>1</VERSIONNUM>
      <FIRST_NAME>ABC</FIRST_NAME>
      <SECOND_NAME>XYZ</SECOND_NAME>      
      <INDIVIDUAL_ALIAS>
        <QUALITY>Good</QUALITY>
        <ALIAS_NAME>abcd</ALIAS_NAME>
      </INDIVIDUAL_ALIAS>
      <INDIVIDUAL_ALIAS>
        <QUALITY>Bad</QUALITY>
        <ALIAS_NAME>ylmn</ALIAS_NAME>
      </INDIVIDUAL_ALIAS>      
    </INDIVIDUAL>
  </INDIVIDUALS>
 </CONSOLIDATED_LIST>

the way I am getting Data, please also see the comments inside code.

 var xml = DownloadString(link); //link refers to XML file on web
            e = XElement.Parse(xml);

            //parentNodeForPerson = "INDIVIDUAL" for above and "sdnEntry" for below;
            var lstIndividuals = e.Descendants(parentNodeForPerson);

            Data _individualData;
            List<Data> individualList = new List<Data>();

            foreach (var individual in lstIndividuals)
            {
                _individualData = new Data();
                //personParentValue1 for above is FIRST_NAME and below is firstName
                _individualData.First_Name = individual.Descendants(personParentValue1)
                    .Any() == true ? individual.Descendants(personParentValue1).First().Value : "";
                //personParentValue2 for above is SECOND_NAME and below is lastName
                _individualData.Last_Name = individual.Descendants(personParentValue2)
                    .Any() == true ? individual.Descendants(personParentValue2).First().Value : "";
                //childNodeForPersonfor above is INDIVIDUAL_ALIAS and below is aka
                var lstIndvidualAlias = individual.Descendants(childNodeForPerson);
                _individualData.Alias = new List<string>();

                foreach (var alias in lstIndvidualAlias)
                {
                    //personChildValue1 for above is ALIAS_NAME and below is lastName & fisrtName
                    if (!String.IsNullOrWhiteSpace(alias.Descendants(personChildValue1).First().Value))
                    {
                        _individualData.Alias.Add(alias.Descendants(personChildValue1)
                            .Any() == true ? alias.Descendants(personChildValue1).First().Value : "");

                    }

                }
                individualList.Add(_individualData);
            }

this is XML where I am not getting data

<List xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
            xmlns="http://tempuri.org/List.xsd">
  <publshInformation>
    <Publish_Date>04/18/2018</Publish_Date>
    <Record_Count>6299</Record_Count>
  </publshInformation>
  <sdnEntry>

    <lastName>ABCD</lastName>
    <fisrtName>XYS</fisrtName>
    <akaList>
      <aka>        
        <category>strong</category>
        <lastName>ABCDT</lastName>
        <fisrtName>XYS</fisrtName>
      </aka>
      <aka>        
        <category>Weak</category>
        <lastName>AssDT</lastName>
        <fisrtName>XYsS</fisrtName>
      </aka>
    </akaList>    
  </sdnEntry>
  </List>

Edit:

Data Class

public class Data
    {
        public string Last_Name { get; set; }
        public string First_Name { get;  set; }       
        public List<string> Alias { get; set; }

    }

I tried your xml(s). I have used xml deserializer and it seems to work fine at my end. Please check the following code:

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.IO;
using System.Reflection;
using System.Xml.Serialization;
using XML1;
using XML2;

namespace ConsoleApplication1
{
    public class Program
    {
        private static void Main(string[] args)
        {
            var list1 = Deserialize<CONSOLIDATED_LIST>(@"CONSOLIDATED_LIST.xml"); // pass the path to your xml here
            var list2 = Deserialize<List>(@"LIST.xml");
        }

        public static T Deserialize<T>(string path)
        {
            T obj;            
            XmlSerializer serializer = new XmlSerializer(typeof(T));
            var reader = new StreamReader(path);
            obj = (T)serializer.Deserialize(reader);
            reader.Close();
            return obj;
        }
    }
}

namespace XML1
{
    [XmlRoot(ElementName = "INDIVIDUAL_ALIAS")]
    public class INDIVIDUAL_ALIAS
    {
        [XmlElement(ElementName = "QUALITY")]
        public string QUALITY { get; set; }
        [XmlElement(ElementName = "ALIAS_NAME")]
        public string ALIAS_NAME { get; set; }
    }

    [XmlRoot(ElementName = "INDIVIDUAL")]
    public class INDIVIDUAL
    {
        [XmlElement(ElementName = "DATAID")]
        public string DATAID { get; set; }
        [XmlElement(ElementName = "VERSIONNUM")]
        public string VERSIONNUM { get; set; }
        [XmlElement(ElementName = "FIRST_NAME")]
        public string FIRST_NAME { get; set; }
        [XmlElement(ElementName = "SECOND_NAME")]
        public string SECOND_NAME { get; set; }
        [XmlElement(ElementName = "INDIVIDUAL_ALIAS")]
        public List<INDIVIDUAL_ALIAS> INDIVIDUAL_ALIAS { get; set; }
    }

    [XmlRoot(ElementName = "INDIVIDUALS")]
    public class INDIVIDUALS
    {
        [XmlElement(ElementName = "INDIVIDUAL")]
        public INDIVIDUAL INDIVIDUAL { get; set; }
    }

    [XmlRoot(ElementName = "CONSOLIDATED_LIST")]
    public class CONSOLIDATED_LIST
    {
        [XmlElement(ElementName = "INDIVIDUALS")]
        public INDIVIDUALS INDIVIDUALS { get; set; }
        [XmlAttribute(AttributeName = "xsi", Namespace = "http://www.w3.org/2000/xmlns/")]
        public string Xsi { get; set; }
        [XmlAttribute(AttributeName = "noNamespaceSchemaLocation", Namespace = "http://www.w3.org/2001/XMLSchema-instance")]
        public string NoNamespaceSchemaLocation { get; set; }
        [XmlAttribute(AttributeName = "dateGenerated")]
        public string DateGenerated { get; set; }
    }

}

namespace XML2
{
    [XmlRoot(ElementName = "publshInformation", Namespace = "http://tempuri.org/List.xsd")]
    public class PublshInformation
    {
        [XmlElement(ElementName = "Publish_Date", Namespace = "http://tempuri.org/List.xsd")]
        public string Publish_Date { get; set; }
        [XmlElement(ElementName = "Record_Count", Namespace = "http://tempuri.org/List.xsd")]
        public string Record_Count { get; set; }
    }

    [XmlRoot(ElementName = "aka", Namespace = "http://tempuri.org/List.xsd")]
    public class Aka
    {
        [XmlElement(ElementName = "category", Namespace = "http://tempuri.org/List.xsd")]
        public string Category { get; set; }
        [XmlElement(ElementName = "lastName", Namespace = "http://tempuri.org/List.xsd")]
        public string LastName { get; set; }
        [XmlElement(ElementName = "fisrtName", Namespace = "http://tempuri.org/List.xsd")]
        public string FisrtName { get; set; }
    }

    [XmlRoot(ElementName = "akaList", Namespace = "http://tempuri.org/List.xsd")]
    public class AkaList
    {
        [XmlElement(ElementName = "aka", Namespace = "http://tempuri.org/List.xsd")]
        public List<Aka> Aka { get; set; }
    }

    [XmlRoot(ElementName = "sdnEntry", Namespace = "http://tempuri.org/List.xsd")]
    public class SdnEntry
    {
        [XmlElement(ElementName = "lastName", Namespace = "http://tempuri.org/List.xsd")]
        public string LastName { get; set; }
        [XmlElement(ElementName = "fisrtName", Namespace = "http://tempuri.org/List.xsd")]
        public string FisrtName { get; set; }
        [XmlElement(ElementName = "akaList", Namespace = "http://tempuri.org/List.xsd")]
        public AkaList AkaList { get; set; }
    }

    [XmlRoot(ElementName = "List", Namespace = "http://tempuri.org/List.xsd")]
    public class List
    {
        [XmlElement(ElementName = "publshInformation", Namespace = "http://tempuri.org/List.xsd")]
        public PublshInformation PublshInformation { get; set; }
        [XmlElement(ElementName = "sdnEntry", Namespace = "http://tempuri.org/List.xsd")]
        public SdnEntry SdnEntry { get; set; }
        [XmlAttribute(AttributeName = "xsi", Namespace = "http://www.w3.org/2000/xmlns/")]
        public string Xsi { get; set; }
        [XmlAttribute(AttributeName = "xmlns")]
        public string Xmlns { get; set; }
    }
}

I personally would prefer the xml deserializer. It would be easy and more maintainable. Your underlying code, would remain the same and the deserialization can be done base on the Type T in your xml

SOLUTION

Yes, that's normal and depends on namespaces. Just edit your LINQ queries and go from the one you used:

var lstIndividuals = e.Descendants(parentNodeForPerson);

to this:

var lstIndividuals = e.Descendants().Where(f => f.Name.LocalName.ToString() == parentNodeForPerson); 

And it will work.


LONG EXPLANATION

The above solution basically ignores all namespace information when retrieving elements by name. The things is that in the first XML you have:

<CONSOLIDATED_LIST xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:noNamespaceSchemaLocation="https://www.example/acb.xsd" 
    dateGenerated="2018-04-19T19:18:41.129-04:00">

Whilst in the second we find

<List xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
      xmlns="http://tempuri.org/List.xsd">

The second XML has two namespaces defined, which throws your LINQ queries out of whack; in fact, if you just removed this from the second XML

xmlns="http://tempuri.org/List.xsd"

your code would work without any changes.

To start with, you can read the answers to What does “xmlns” in XML mean? , especially this one . In short, namespaces can be useful eg when you combine XMLs from different sources and you get elements with the same name: by prepending a namespace, elements with the same name will be seen as different:

<namespaceA:elementName>blah     </namespaceA:elementName>
<namespaceB:elementName>blah blah</namespaceB:elementName>

In your XML you have two namespaces but, when looking for Descendants you don't specify which namespace your elements belong to. Another solution, perhaps more robust than the quick one above, would be specifying the namespace the name of an element belongs to, eg like the OP does in this question , using XName.Get : in your case, you would get the nodes of the second XML this way:

var lstIndividuals = e.Descendants(XName.Get(parentNodeForPerson, "http://tempuri.org/List.xsd"));

which works because the URI specified ( http://tempuri.org/List.xsd ) is the one of the default namespace you declared:

 xmlns="http://tempuri.org/List.xsd"

and, since your elements don't have any prefix, it's the namespace your elements belong to; if they belonged to the other namespace you defined, ie xsi , their names would be eg xsi:sdnEntry .

Of course, since there's no default namespace in your first XML declaration but only the xsi namespace (which refers to a different URI), you would need to pass its URI to XName.Get and adapt your code. In other words, you should make the URL parametric and pass it to your method, rather than hardcoding it like I did.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM