2

I have and so far failed at implementing 2 approaches to parse an xml tree and cast it, along with its children, into objects. I have tried object serialization as explain here and I have used Linq as explained by the accepted answer here.

With the first approach (deserialization), it works up until the List<ExtensionItem> attribute of IndividualOrEntityValidationExtensions not getting its values assigned (i.e it remains null).

With the second approach (LINQ), I get this error pertaining to the entire OutputFilePaths =... block.

/Users/g-wizkiz/Projects/XmlParser/XmlParser/Program.cs(68,68): Error CS0266: Cannot implicitly convert type 'System.Collections.Generic.IEnumerable<XmlParser.models.OutputFilePaths>' to 'XmlParser.models.OutputFilePaths'. An explicit conversion exists (are you missing a cast?) (CS0266) (XmlParser)

I'm happy to go with whichever works, though I do feel like LINQ is a more elegant approach.

I will show my XML and class structure, followed by the respective code blocks.

XML

<?xml version="1.0" encoding="UTF-8" ?>
<ParameterList>
    <Parameters>
        <NumberOfThreads>10</NumberOfThreads>
        <InputFilePath>C:\Input.dat</InputFilePath>
         <OutputFilePaths>
            <NameFile>name.txt</NameFile>
            <ValidationFile>validation.txt</ValidationFile>
            <AuditLog>audit.txt</AuditLog>
        </OutputFilePaths>
        <DictionaryExtensions>
            <IndividualOrEntityValidationExtensions>
                <ExtensionItem>
                    <Type>dictType1</Type>
                    <Path>dictPath1</Path>
                </ExtensionItem>
                  <ExtensionItem>
                    <Type>dictType2</Type>
                    <Path>dictPat2</Path>
                </ExtensionItem>
            </IndividualOrEntityValidationExtensions>
        </DictionaryExtensions>
    </Parameters>
</ParameterList>

Classes

[XmlRoot("Parameters")]
    public class Parameters
    {
        public int NumberOfThreads { get; set; }
        public string InputFilePath { get; set; }
        public OutputFilePaths outputFilePaths { get; set; }
        public DictionaryExtensions DictionaryExtensions { get; set; }
    }

  public class OutputFilePaths
    {
        public string NameFile { get; set; }
        public string ValidationFile { get; set; }
        public string AuditLog { get; set; }
    }

        public class DictionaryExtensions
        {
        [XmlElement("IndividualOrEntityValidationExtensions")]
        public IndividualOrEntityValidationExtension IndividualOrEntityValidationExtensions { get; set; }
      }

       public class IndividualOrEntityValidationExtension
        {
            [XmlArrayItem("ExtensionItem")]
            public List<ExtensionItem> ExtensionItem { get; set; }
        }

    public class ExtensionItem
     {
        [XmlAttribute("Type")]
        public string Type { get; set; }
        [XmlAttribute("Path")]
        public string Path { get; set; }
    }

Object deserialization approach

string xmlString = System.IO.File.ReadAllText(@"/Users/g-wizkiz/Projects/XmlParser/XmlParser/parameters.xml");
            XmlSerializer serializer = new XmlSerializer(typeof(List<Parameters>), new XmlRootAttribute("ParameterList"));
            StringReader stringReader = new StringReader(xmlString);
            List<Parameters> parameters = (List<Parameters>)serializer.Deserialize(stringReader)

LINQ approach

XDocument doc = XDocument.Parse(xmlString);
            IEnumerable<Parameters> result = from c in doc.Descendants("Parameters")
                                             select new Parameters()
                                             {
                                                 NumberOfThreads = (int)c.Attribute("NumberOfThreads"),
                                                 InputFilePath = (string)c.Attribute("InputFilePath"),
                                                 outputFilePaths = from f in c.Descendants("OutputFilePaths")
                                                                   select new OutputFilePaths()
                                                                   {
                                                                       ValidationFile = (string)f.Attribute("ValidationFile"),
                                                                       AuditLog = (string)f.Attribute("AuditLog"),
                                                                       NameFile = (string)f.Attribute("NameFile")
                                                                   }
                                             };

Cheers!

Glenncito
  • 902
  • 1
  • 10
  • 23
  • Try just without separate class: [XmlElement("IndividualOrEntityValidationExtensions")][XmlArrayItem("ExtensionItem")] public List IndividualOrEntityValidationExtensions { get; set; } – eocron May 12 '20 at 08:48
  • @eocron I'm not sure I follow. Am I modifying this DictionaryExtension.cs? If so, how should I modify IndividualOrEntityValidationExtensions which has the List attribute currently? – Glenncito May 12 '20 at 08:55

3 Answers3

1

I'm thinking the problem is related to the Parameter class where the OutputFilePaths is a single entity and not an IEnumerable<OutputFilePaths> or if you are expecting only one outputfilepath then select the .FirstOrDefault() in your linq query (be prepared for null values).

Richárd Baldauf
  • 1,068
  • 2
  • 10
  • 24
  • 1
    The code was fully tested and works. Since output paths was not an array I had to use FirstOrDefault since f was returning an array. – jdweng May 12 '20 at 13:16
1

Testing locally, this works fine as the only change:

public class IndividualOrEntityValidationExtension
{
    [XmlElement("ExtensionItem")]
    public List<ExtensionItem> ExtensionItem { get; set; }
}

However, you can remove a level in the hierarchy if you prefer - throwing away the IndividualOrEntityValidationExtension type completely:

public class DictionaryExtensions
{
    [XmlArray("IndividualOrEntityValidationExtensions")]
    [XmlArrayItem("ExtensionItem")]
    public List<ExtensionItem> ExtensionItems { get; set; }
}
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
1

I fixed your linq. The nodes are elements not attributes.

            XDocument doc = XDocument.Parse(xmlString);
            IEnumerable<Parameters> result = (from c in doc.Descendants("Parameters")
                select new Parameters()
                {
                    NumberOfThreads = (int)c.Element("NumberOfThreads"),
                    InputFilePath = (string)c.Element("InputFilePath"),
                    outputFilePaths = (from f in c.Descendants("OutputFilePaths")  
                                    select new OutputFilePaths()
                                    {
                                        ValidationFile = (string)f.Element("ValidationFile"),
                                        AuditLog = (string)f.Element("AuditLog"),
                                        NameFile = (string)f.Element("NameFile")
                                    }).FirstOrDefault()
                }).ToList();
jdweng
  • 33,250
  • 2
  • 15
  • 20