Retrieving sub elements of xml using Xdocument

I want to parse an XML file like this:

 <?xml version="1.0" encoding="utf-8" ?> 
    <database name="myDb">
      <table name="myTable">
        <field name="Code" type="int" size="0" identity="true" primarykey="true" description="کد شناسه" reference=""></field>
        <field name="Name" type="nvarchar" size="50" identity="false" primarykey="false" description="نام شخص" reference=""></field>
      </table>
      <table name="yourTable">
        <field name="Code" type="int" size="0" identity="true" primarykey="true" description="کد شناسه" reference=""></field>
        <field name="Title" type="nvarchar" size="50" identity="false" primarykey="false" description="نام شخص" reference=""></field>
      </table>
    </database>

The problem is that in my inner foreach it will parse 4 fields instead of 2 fields related to each table. how to change the code to read only the fields for current table?

XDocument xdoc = XDocument.Load("d:\\tables.xml");
        foreach (XNode table in xdoc.Descendants("database").Nodes())
        {
            fields = "";
            tableName = XElement.Parse(table.ToString()).Attribute("name").Value;
            //XElement xE = XElement.Parse(table.ToString());
            //foreach (XElement e in xE.Elements())
            foreach (XNode field in xdoc.Descendants("table").Nodes())
            {
                fieldName = XElement.Parse(field.ToString()).Attribute("name").Value;
                type = XElement.Parse(field.ToString()).Attribute("type").Value;
                size = XElement.Parse(field.ToString()).Attribute("size").Value;
                identity = XElement.Parse(field.ToString()).Attribute("identity").Value;
                primarykey = XElement.Parse(field.ToString()).Attribute("primarykey").Value;
                description = XElement.Parse(field.ToString()).Attribute("description").Value;
                reference = XElement.Parse(field.ToString()).Attribute("reference").Value;

                if (identity == "true") identity = "identity";
                if (primarykey == "true") primarykey = "primary key";

                if (isChar(type))
                {
                    fields += string.Format(fieldCharTemplate, fieldName, type, size);
                }
                else
                {
                    fields += string.Format(fieldNonCharTemplate, fieldName, type, identity,primarykey);
                }
                //var y = x.Element("type");
            }
            sql = string.Format(tableTemplate, tableName, fields);
            Response.Write(sql);
        }
Jon Skeet
people
quotationmark

This is the problem:

foreach (XNode field in xdoc.Descendants("table").Nodes())

That's looking for all nodes under all table elements. You don't want that.

You've already got the table you're looking at as an XElement - so use that. It requires that your table variable is an XElement rather than just XNode, but that's what you really want anyway... and it means you don't have to reparse it to get the table name. I would write your code as:

foreach (XElement table in xdoc.Root.Elements("table"))
{
    string tableName = (string) table.Attribute("name");
    StringBuilder fields = new StringBuilder();
    foreach (XElement field in table.Elements("field"))
    {
        string fieldName = (string) field.Attribute("name");
        string type = (string) field.Attribute("type");
        // Consider casting to int instead...
        string size = (string) field.Attribute("size");
        bool identity = (bool) field.Attribute("identity");
        bool primaryKey = (bool) field.Attribute("primarykey");
        string description = (string) field.Attribute("description");
        string reference = (string) field.Attribute("reference");
        // Append to fields here
    }
}

Note how much simpler it is to get to the attributes when you already have an element. You really shouldn't need to do multiple parse operations in the vast majority of cases. Also note how I've used the conversion to bool for primarykey and identity. Again, this is cleaner than testing the string manually.

people

See more on this question at Stackoverflow