question

FestusHagen-0831 avatar image
0 Votes"
FestusHagen-0831 asked FestusHagen-0831 commented

Linq Xml with node attributes and IList<> descendants

Hi all,

Frustrated, having a tough time with linq ...

Read only xml configuration file with IList<> of objects.

The goal is to pass in an id (by constructor or routine), it reads that id's configuration from xml into properties of this class that can be publicly read.

Design is open, this is the rut my mind is currently in ...

 <Root>
     <File FileID="0x01" Size="0x00034100" Description="Test 1 Description">
         <Sections>
             <Section Index="1" Start="0x00000000" Size="0x00000100">Description 1</Section>
             <Section Index="2" Start="0x00000100" Size="0x00001000">Description 2</Section>
             <Section Index="3" Start="0x00001100" Size="0x00010000">Description 3</Section>
         </Sections>
     </File>
     <File FileID="0x02" Size="0x00031100" Description="Test 2 Description">
         <Sections>
             <Section Index="1" Start="0x00000000" Size="0x00000100">1 Description</Section>
             <Section Index="2" Start="0x00000100" Size="0x00001000">2 Description</Section>
             <Section Index="3" Start="0x00001100" Size="0x00010000">3 Description</Section>
         </Sections>
     </File>
 </Root>
    
     public class Section
     {
         // Note: The collection must remain in order, if the list keeps order, we don't need Index
         public UInt32 Index { get; set; }       // Node Section Attribute("Index")
         public UInt32 Start { get; set; }       // Node Section Attribute("Start")
         public UInt32 Size { get; set; }        // Node Section Attribute("Size")
         public string Description { get; set; } // Node Section Value
     }
    
     public class MyFiletype
     {
         public UInt32 FileID { get; set; }           // Node File Attribute("FileID")
         public UInt32 Size { get; set; }             // Node File Attribute("Size")
         public string Description { get; set; }      // Node File Attribute("Description")
         public IList<Section> Sections { get; set; } // Collection of Section objects
    
         public MyFiletype(UInt32 id)
         {
             string filename = @"..\..\Xml\Sections.xml";
    
             var doc = XDocument.Load(filename);  // XDocument
             var result = doc.Descendants("File") // IEnumerable<XElement>
                             // Find the chosen node, we only want one and it's descendants!
                             .Where(i => Convert.ToUInt32(i.Attribute("FileID").Value, 16) == id)
                             // This is where I get stumped ... Have read and tried so many ways I think I'm even more lost!
                             // I can get one or the other, not both Node File's Attributes and the Sections Collection.
                             .FirstOrDefault().Value; // test bit garbage
         }
     }

 /*   
         // It's use ...
         UInt32 id = 0x02;
         MyFiletype myclass = new MyFiletype(id);
         //Debug.WriteLine($"File Description: {myclass.Description}");
 */    

Thank you
fh

dotnet-csharp
· 2
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

Still waiting on an answer ...

0 Votes 0 ·
cooldadtx avatar image
0 Votes"
cooldadtx answered

It sounds like you're searching for files, not sections. So your LINQ query should return the file element that matches the ID you want. From there you can access its attributes and its child sections.

I modified your code to use a helper type to handle the XML document as I find it cleaner. Personally I would also move the XML parsing to this helper class and leave the original data classes clean but that is a preference. The hardest issue here is that your FileID is a hex number so you have to convert it properly.

public class MyFiletype
{
    public UInt32 FileID { get; set; }           // Node File Attribute("FileID")
    public UInt32 Size { get; set; }             // Node File Attribute("Size")
    public string Description { get; set; }      // Node File Attribute("Description")
    public IList<Section> Sections  { get; set; } // Collection of Section objects

    public MyFiletype ( XElement element )
    {
        //Parse the data now or look it up as needed...
        _element = element;
    }

    private readonly XElement _element;
}

public class FileXmlDocument
{
    public FileXmlDocument ( string filename )
    {
        _doc = XDocument.Load(filename);
    }

    public MyFiletype GetFile ( UInt32 id )
    {
        var element = _doc.Descendants("File").FirstOrDefault(x => ParseHexValue(x.Attribute("FileID")?.Value) == id);

        return (element != null) ? new MyFiletype(element) : null;
    }

    private uint ParseHexValue ( string value )
    {
        if (String.IsNullOrEmpty(value))
            return 0;

        if (value.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
            return UInt32.TryParse(value.Substring(2), NumberStyles.HexNumber, null, out var result2) ? result2 : 0;

        return UInt32.TryParse(value, out var result) ? result : 0;
    }

    private readonly XDocument _doc;
}
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

FestusHagen-0831 avatar image
0 Votes"
FestusHagen-0831 answered FestusHagen-0831 commented

This POS site and it's 1600 byte limit is just wacked!
So instead of multiple posts, I attached my own answer ...

198417-myfiletype.txt


If the technique is considered wrong, poor, bad design or whatever I would love to know!


myfiletype.txt (2.5 KiB)
· 2
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

@FestusHagen-0831, based on my test, I can get some output from your code. Then, Could you tell me which data you want to get from the xml file?
tested result for me:
198720-image.png


0 Votes 0 ·
image.png (2.8 KiB)

As I stated, I answered my own question, I am now getting the data I want ...
That sample is trimmed down trying to fit 1600 bytes! I gave up.

The logic is clear, the data is clear ...

I'll repeat the requirements.

The class that uses MyFileType currently constructs MyFileType like,

             UInt32 id = 0x01;
             MyFileType myobject = MyFileType.Create(id);

And expects MyFileType to have the following elements,

     public class MyFileType
     {
         public UInt32 FileID { get; set; }           // Node File Attribute("FileID")
         public UInt32 Size { get; set; }             // Node File Attribute("Size")
         public string Description { get; set; }      // Node File Attribute("Description")
         public IList<Section> Sections { get; set; } // Collection of Section objects
     }

     public class Section
     {
         // Note: The collection must remain in order, if the list keeps order, we don't need Index
         public UInt32 Index { get; set; }       // Node Section Attribute("Index")
         public UInt32 Start { get; set; }       // Node Section Attribute("Start")
         public UInt32 Size { get; set; }        // Node Section Attribute("Size")
         public string Description { get; set; } // Node Section Value
     }

I can change the MyFileType <--> Parent class interface if a better technique warrants a change.

The Xml is basically static data ...

0 Votes 0 ·