Search code examples
c#xmllinq

C# Getting all element values below a specific element


I have an XML file full of Location elements, with all of its child elements containing information that I need to essentially put into a list, or ideally put into a Location type object.

Here I define my location class:

public class Location
    {
        /* <Summary>
         * Class that describes a location object in GPL. This class is 
         *  used to get/put location objects from TCS
         */
        #region Private Declarations
        private string _msg;
        private string _msgIn;
        private string[] _msgRes;
        private TcpIpComm _client { get; set; }
        #endregion

        public Location(TcpIpComm commObj, string locName)
        {
            _client = commObj;
            Name = locName;
        }

        public Location(TcpIpComm commObj)
        {
            _client = commObj;
        }

        public Location()
        {

        }

        public string Name { get; set; }
        public int Index { get; set; }
        public float Joint1 { get; set; }
        public float Joint2 { get; set; }
        public float Joint3 { get; set; }
        public float Joint4 { get; set; }
        public float Joint5 { get; set; }
        public float Joint6 { get; set; }
        public float Joint7 { get; set; }
        public float ZClearance = 0;
        public LocType Type { get; set; }

        /// <summary>
        /// 0 = cartesian,
        /// 1 = angles
        /// </summary>
        public enum LocType
        {
            Cartesian = 0,
            Angles = 1
        }
}

Here is an example of what my XML files would look like:

<?xml version="1.0" encoding="utf-8"?>
<ArrayOfLocations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Location>
    <Name>LocName0</Name>
    <Type>Angles</Type>
    <Index>0</Index>
    <ZClearance>0</ZClearance>
    <Joint1>0</Joint1>
    <Joint2>0</Joint2>
    <Joint3>0</Joint3>
    <Joint4>0</Joint4>
    <Joint5>0</Joint5>
    <joint6>0</joint6>
    <Joint6>0</Joint6>
  </Location>
  <Location>
    <Name>LocName1</Name>
    <Type>Angles</Type>
    <Index>0</Index>
    <ZClearance>0</ZClearance>
    <Joint1>1</Joint1>
    <Joint2>1</Joint2>
    <Joint3>1</Joint3>
    <Joint4>1</Joint4>
    <Joint5>1</Joint5>
    <joint6>1</joint6>
    <Joint6>1</Joint6>
  </Location>
  <Location>
    <Name>LocName2</Name>
    <Type>Angles</Type>
    <Index>0</Index>
    <ZClearance>0</ZClearance>
    <Joint1>2</Joint1>
    <Joint2>2</Joint2>
    <Joint3>2</Joint3>
    <Joint4>2</Joint4>
    <Joint5>2</Joint5>
    <joint6>2</joint6>
    <Joint6>2</Joint6>
  </Location>

Now I want to be able to enter a Location name into a method (ex: LocName0), and have all the values underneath that Name element returned to me in a list (Type, Index, ZClearance, Joint1, Joint2, etc...). I'm struggling with this one because the Name element isn't a parent element of anything. It is the child element of Location, so the Elements underneath Name aren't descendants, AFAIK.

Here is one method I have written in an attempt to do what I have described:

        public static void GetTeachPoint(string teachName, string filePath)
        {
            var listOfElems = XElement.Parse(filePath)
                .Elements() // gets all elements under root
                .Where(x => x.Name.LocalName.StartsWith(teachName))
                .SelectMany(x => x.Elements())
                .Select(x => x.Value)
                .ToList();
            foreach(string item in listOfElems){
                MessageBox.Show(item);
            }
        }

However when I pass the location name and filepath into the method, I receive a System.Xml.XmlException: 'Data at the root level is invalid. Line 1, position 1.'


Solution

  • This doesn't do what you think it does:

    XElement.Parse(filePath)
    

    That's trying to parse filePath as if it were actually XML. I suspect you meant to use XElement.Load which loads XML from a file - or you could use XDocument.Load, potentially.

    That will get over the initial problem, but your query is still broken. You want (I believe) something like:

    var elements = XElement.Load(filePath)
        .Elements("Location")
        .Elements("Name")
        .Where(x => x.Value == teachName)
        .SelectMany(x => x.Parent.Elements());