score:2

Accepted answer

i don't know if there are any build-in functions to flaten the xml, but i'm very concerned they can fit your needs, because some attributes must be ignored according to your example.

you better use linq to xml which allows transforming such documents very easy and with not a lot of code. see my solution:

string origxml = "<rates id=\"1\" rateseffectivatedate=\"27/02/2014\">\n  <type name=\"type1\" code=\"a\">\n    <del id=\"d1\">\n      <field1>10</field1>\n      <field2>20</field2>\n      <field3>30</field3>\n      <field4>40</field4>\n    </del>\n    <del id=\"d2\">\n      <field1>50</field1>\n      <field2>60</field2>\n      <field3>70</field3>\n      <field4>80</field4>\n    </del>\n  </type>\n  <type name=\"type2\" code=\"b\">\n    <del id=\"d1\">\n      <field1>110</field1>\n      <field2>120</field2>\n      <field3>130</field3>\n      <field4>140</field4>\n    </del>\n    <del id=\"d3\">\n      <field1>150</field1>\n      <field2>160</field2>\n      <field3>170</field3>\n      <field4>180</field4>\n    </del>\n  </type>\n</rates>";
var xdoc = xdocument.parse(origxml);

var resdoc = new xdocument(
    new xelement("rates",
                    xdoc.element("rates")
                        .elements("type")
                        .selectmany(typeel =>
                                    typeel.elements("del")
                                        .selectmany(delel =>
                                                    delel.elements()
                                                        .select(fieldel =>
                                                                new xelement("rate",
                                                                                new xelement("id", typeel.attribute("name").value),
                                                                                new xelement("code", typeel.attribute("code").value),
                                                                                new xelement("delid", delel.attribute("id").value),
                                                                                new xelement(fieldel.name, fieldel.value)))))
        ));

resdoc.save("transformeddoc.xml", saveoptions.none);

the output is exactly what you provided for your example.

score:2

you can do it like this:

var xmldocument = xdocument.load("path");

var rates = new list<xelement>();

foreach (var type in xmldocument.descendants("type"))
{
    foreach (var del in type.elements("del"))
    {
          foreach (var field in del.elements())
          {
              xelement rate = new xelement("rate",
                        new xelement("id", (string) type.attribute("name")),
                        new xelement("code", (string) type.attribute("code")),
                        new xelement("delid", (string) del.attribute("id")),
                        new xelement(field.name, (string) field));
              rates.add(rate);
          }
    }
}

xelement root = new xelement("rates");
root.add(rates);
root.save("newfile.xml");

another way using linq instead of loops but i think this is less readable

var xmldocument = xdocument.load("path");

var newxml = new xelement("rates",
            xmldocument.descendants()
                .where(x => x.name.tostring().startswith("field"))
                .select(
                    x =>
                        new xelement("rate",
                            new xelement("id", (string) x.parent.parent.attribute("name")),
                            new xelement("code", (string) x.parent.parent.attribute("code")),
                            new xelement("delid", (string) x.parent.attribute("id")),
                            new xelement(x.name, (string) x))));

newxml.save("newfile.xml");

Related Query