0

I'm sorry for advanced if this question has been repeated, but from my search there hasn't been any appropriate answers for my particular error.

So to the question, I've currently got an excel document with several rows of XML within the spread sheet. What I am currently doing is creating a console application within C# to then filter the XML in to readable data. I've currently able to extract the documents content and now I'm running through the code to break down the data.

So for the xml structure:

the xml has several fields, within each field there is a label and value fields. So I've taken the route to create a list of all the nodes (e.g.fields) and try to extract the data with XElement. Also I know all the labels so what I will do is use the labels to try to collect the values.

foreach(DataRow row in result.Tables[0].Rows)
   {
       var XmlData = row.ItemArray[2].ToString();
       XElement doc = XElement.Parse(XmlData);
       foreach (XElement element in doc.Elements("fields"))
          {
              var userEntry = new FieldEntryModel();

              //element.Value.ToList();
              var elementNodes = element.Nodes().ToList();
              var nodeBreakdown = element.Nodes().OfType<XElement>();

              foreach (var something in nodeBreakdown)
                 {
                    var willthiswork = something.DescendantNodes().OfType<XElement>();                             
                    foreach (var item in willthiswork) {
                       if (item.Value != null)
                           {
                                if (item.Value.ToString().Contains("Forename"))
                                    //Console.WriteLine(item..ToString());
                                //userEntry.Forename = item.Value.ToString();
                             }
                    }                           
            }
    }

I start to get confused between the naming conversions and the object types to obtain the data.

SAMPLE XML:

<fields>
   <field>
      <label>
        Name
      <label>
      <value>
        Test
      </value>
   </field>
   <field>
      <label>
        job
      <label>
      <value>
        developer
      </value>
   </field>
   <field>
      <label>
        address
      <label>
      <value>
        1 Test
      </value>
   </field>
   <field>
      <label>
        Name
      <label>
      <value>
        Test
      </value>
   </field>
 </fields>

I would then store this data in my field entry model so that is:

 public class FieldEntryModel
{
    public string Name {get;set;}
    public string Job {get;set;}
    public string Address{get;set;}
}
jsg
  • 1,224
  • 7
  • 22
  • 44

1 Answers1

2

I believe something like this may work for you. Since you seem to have duplicate labels in your fields (e.g. "<label>Name</label>" appears twice), I've used FirstOrDefault here. You should investigate why there are duplicates and handle appropriately. If there were no duplicates, SingleOrDefault would be the better choice.

var fieldElements = doc.Elements("fields").Elements("field");
var nameElement = fieldElements.FirstOrDefault(e => e.Element("label")?.Value == "Name");
var jobElement = fieldElements.FirstOrDefault(e => e.Element("label")?.Value == "job");
var addressElement = fieldElements.FirstOrDefault(e => e.Element("label")?.Value == "address");
var model = new FieldEntryModel
            {
                Name = nameElement?.Element("value")?.Value,
                Job = jobElement?.Element("value")?.Value,
                Address = addressElement?.Element("value")?.Value,
            };

Note that I've used the null-conditional operator from C# 6 (Visual Studio 2015+) in this.

Michael Gunter
  • 12,528
  • 1
  • 24
  • 58
  • This worked until it hit the name with an exception error "system.invalidOperationException" occurred in System.Core.Dll – jsg Jun 27 '16 at 15:56
  • I had to tweak the answer a little bit. What's there now should work. – Michael Gunter Jun 27 '16 at 15:57
  • Which line? If it's on one of the SingleOrDefault lines, it's because there are duplicate field labels. – Michael Gunter Jun 27 '16 at 15:58
  • Modified slightly, since the Name value is capitalized. – Chuck Savage Jun 27 '16 at 15:59
  • On the "nameElement" line – jsg Jun 27 '16 at 15:59
  • The error you're getting is from the SingleOrDefault call. It happens when there is more than one item in the enumeration that matches the predicate. This probably means you have duplicate labels. How should that be handled? – Michael Gunter Jun 27 '16 at 16:00
  • Actually, sorry. It happens when there are 0 matching items or more than 1 matching item. Chuck's edit (capitalizing "Name") may have fixed this for you. If not, it's a either duplicate field labels or a missing field. – Michael Gunter Jun 27 '16 at 16:02
  • Yeh its possible that it could be duplicate fields labels with name. – jsg Jun 27 '16 at 16:03
  • In that case, you may try using FirstOrDefault instead of SingleOrDefault. However, you should probably understand why there are duplicate labels and handle them as appropriate. – Michael Gunter Jun 27 '16 at 16:04
  • 1
    Not familiar with .net 6. Is `.Elements("fields").Elements("field");` The same as `.Elements('fields").SelectMany(f => f.Elements('field"))` would give multiple labels with `Name` if there were more than one `fields` label. – Chuck Savage Jun 27 '16 at 16:06
  • Thank you ChuckSavage and MichaelGunter that worked perfectly, i guess i'll have to monitor this but thank you for your help! – jsg Jun 27 '16 at 16:08
  • Note you can avoid the null conditionals by using the built in explicit conversion operators. This is even more useful when you need to convert to e.g. integer, date: `(string)e.Element("label")`. – Charles Mager Jun 27 '16 at 16:09
  • @ChuckSavage: Agreed. However, in cases where there's only one container element, I usually find it easier to do `.Elements("container").Elements("item")` because that always yields a non-null. The other one-line option is `.Element("container")?.Elements("item")`, but then you have to use more null-conditional operators. Of course, if there are multiple container elements, this exacerbates the original problem. :) – Michael Gunter Jun 27 '16 at 16:10
  • Or create a list of `FieldEntryModels`. Anyway, +1, learned some new features to the language. – Chuck Savage Jun 27 '16 at 16:12