none
Get Parents of leaf nodes using (LINQ to XML) or mapping to better classes? RRS feed

  • Question

  • Am trying to visualize a tree from this deeply nested XML data but confused. Should I be mapping the XML objects to my own class (that simplifies the data structure into nodes with links (to children if the node has them), or just query direct using LINQ?

    Realize this may seem very simple for many but have spent almost 2 days on it now so would appreciate some direction. 

    Here is the XML data. Note each <Clade> element has child elements that are really attributes (eg "name", "branch_length", etc) as well as in some cases, child <clade> elements:

    <clade> <name>Life on Earth</name> <branch_length>1.0</branch_length> <tol>1</tol> <age_mya>3000.167</age_mya> <orig_branch_length>1.0</orig_branch_length> <clade> <name>unnamed node</name> <branch_length>0.3888888888888889</branch_length> <age_mya>2999.778</age_mya> <pw_index>1</pw_index> <orig_branch_length>0.3888888888888889</orig_branch_length> <clade> <name>Eukaryotes</name> <branch_length>798.7777777777778</branch_length> <tol>3</tol> <age_mya>2201.0</age_mya> <pw_index>2</pw_index> <orig_branch_length>798.7777777777778</orig_branch_length> <clade> <name>unnamed node</name> <branch_length>1.6666666666666667</branch_length> <age_mya>2199.333</age_mya> <pw_index>1</pw_index> <orig_branch_length>1.6666666666666667</orig_branch_length> <clade> <name>Opisthokonts</name> <branch_length>579.3333333333335</branch_length> <tol>2372</tol> <age_mya>1620.0</age_mya> <pw_index>1</pw_index> <orig_branch_length>579.3333333333335</orig_branch_length> <clade> <name>Animals</name> <branch_length>870.0</branch_length> <tol>2374</tol> <age_mya>750.0</age_mya> <pw_index>1</pw_index> <orig_branch_length>870.0</orig_branch_length> <clade> <name>Bilateria</name> ... etc

    To graph a tree, i need to get all the leaf objects (i.e. species) and work up the tree. However, this query doesn't return child <clade> objects / species, but rather their attributes (because of the data structure):

    XDocument xmlTree = XDocument.Load(Application.streamingAssetsPath + "/tree.xml");
    foreach (XElement childElement in xmlTree.Descendants().Where(d => !d.Elements().Any())) 
    {
    Console.WriteLine(childElement);

    Returns

    <name>Banana</name>              
    <branch_length>304.8128342245987</branch_length>
    <tol>21506</tol>
    <age_mya>0.0</age_mya>
    <pw_index>1</pw_index>                     
    ...etc for some 4000 lines

    Is there a way for LINQ to somehow understand that the <Clade> is the node and attach subelements <name>, <branch_length> etc to it – or do I need to map directly to classes?

    If the latter – how can i do this whilst incorporating the child and parent relationships?

    Thus far I have:

    using System.Collections;
    using System.Collections.Generic;
    using System.Xml.Linq;
    using System.Linq;
    
    public class PlotTree : MonoBehaviour 
    {
        public Node nodePrefab; //represent each species 
        public float scale = 0.1f;
        public float ySpacing = 0.1f; // spacing
    
        void Start()
        {
            // load XML file into memory
            XDocument xmlTree = XDocument.Load(Application.streamingAssetsPath + "/tree.xml");
    
            // create and populate list of nodes/species/clades from XDocument 
            // BUT HOW TO ADD LISTS (not sure...)
            IEnumerable<Clade> clades = from n in xmlTree.Descendants("clade")
                                        select new Clade(
                                                             (string)n.Element("name"),
                                                             (float)n.Element("branch_length"),
                                                             (float)n.Element("age_mya"),
                                                             (float)n.Element("orig_branch_length"));
        }
        class Clade
        {
            public string name { get; set; }
            public float branch_length { get; set; }
            public float age_mya { get; set; }
            public float orig_branch_length { get; set; }
    
            //public List<Clade> children { get; set; }
    
            public Clade(string n, float b, float a, float o) //List<Clade> c
            {
                name = n;
                branch_length = b;
                age_mya = a;
                orig_branch_length = o;
                //children = c;
            }
        }

    Of course this is insufficient because it doesn't let me query all species without children...
    Many thanks to any generous person who can point me in the right direction. 

    Max

    Saturday, August 25, 2018 1:37 PM

All replies

  • Hi MaxXR,

    Thank you for posting here.

    For your question, do you mean you want to get the xml nodes and show it like a tree? If yes, you could try the code below.

    XDocument doc = XDocument.Load(@"XML.xml");
    string tree = doc.Root.DescendantsAndSelf().Aggregate("", (bc, n) => bc + n.Ancestors().Aggregate("", (ac, m) => (m.ElementsAfterSelf().Any() ? "| " : "  ") + ac, ac => ac + (n.ElementsAfterSelf().Any() ? "+-" : "\\-")) + n.Name + "\n");
                

    I use a simple xml file for reference.

    <?xml version="1.0" encoding="utf-8" ?>
    <root>
      <Brand name="Brand1">
        <product name="Product1" />
        <product name="Product2" />
      </Brand>
      <Brand name="Brand2">
        <product name="Product3" />
        <product name="Product4" />
      </Brand>
    </root>
    


    If I misunderstand what you want, please feel free to contact us.

    Best Regards,

    Wendy


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    • Proposed as answer by Stanly Fan Thursday, August 30, 2018 6:39 AM
    Tuesday, August 28, 2018 2:50 AM
    Moderator