1

I need to create an XML file with line breaks and tabs in Attributes and on few tags as well. So I tried like below.

string xmlID = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<PersonLib Version=\"1.0\"></PersonLib>";
XDocument doc = XDocument.Parse(xmlID);//, LoadOptions.PreserveWhitespace);
XElement device = doc.Root;
using (StringWriter str = new StringWriter())
using (XmlTextWriter xml = new XmlTextWriter(str))
{
    xml.Formatting=Formatting.Indented;

    xml.WriteStartElement("Details");
    xml.WriteWhitespace("\n\t");
    xml.WriteStartElement("Name");
    xml.WriteWhitespace("\n\t\t");
    xml.WriteStartElement("JohnDoe");
    xml.WriteAttributeString("DOB", "10");
    xml.WriteAttributeString("FirstName", "20");
    xml.WriteAttributeString("LastName", "40");
    xml.WriteAttributeString("\n\t\t\tAddress", "50");
    xml.WriteAttributeString("\n\t\t\tPhoneNum", "60");
    xml.WriteAttributeString("\n\t\t\tCity", "70");
    xml.WriteAttributeString("\n\t\t\tState", "80");
    xml.WriteAttributeString("\n\t\t\tCountry", "90");
    //xml.WriteWhitespace("\n\t\t");
    xml.WriteEndElement();
    xml.WriteWhitespace("\n\t");
    xml.WriteEndElement();
    xml.WriteWhitespace("\n");
    xml.WriteEndElement();
    Console.WriteLine(str);

    device.Add(XElement.Parse(str.ToString(), LoadOptions.PreserveWhitespace));

    File.WriteAllText("MyXML.xml", device.ToString());

I can get the XML generated in format I need but the issue comes when I try to add it to the parent XMLElement device in this case. The formatting is all gone despite LoadOptions.PreserveWhitespace.

I get

<PersonLib Version="1.0">
  <Details>
    <Name>
        <JohnDoe DOB="10" FirstName="20" LastName="40" Address="50" PhoneNum="60" City="70" State="80" Country"90" />
    </Name>
</Details>
</PersonLib >

while I need

<PersonLib Version="1.0">
    <Details>
        <Name>
            <JohnDoe DOB="10" FirstName="20" LastName="40"
                        Address="50"
                        PhoneNum="60"
                        City="70"
                        State="80"
                        Country="90" />
        </Name>
    </Details>
</PersonLib >

Not sure what am I missing.

Programmerzzz
  • 1,237
  • 21
  • 48
  • Ohh its a bad example. My elements dont have spaces in the names..i just corrected it – Programmerzzz Mar 28 '18 at 00:34
  • If you just want your xml formatted with attributes on a new line you might take a look at this question: https://stackoverflow.com/questions/8237165/how-do-i-set-the-settings-property-in-xmltextwriter-so-that-i-can-write-each-xm – Dweeberly Mar 28 '18 at 02:38
  • I would ask why you feel the need to have such an output. It is no more XML than one that has no formatting between attributes. Are you using a non-XML process to attempt to interpret it? – Kevin Brown Mar 28 '18 at 04:13
  • This xml file is fed into a system that only accepts the mentioned formatting. Its a third party tool and customer is stringent on the format of the input given to that tool. So we should have such formatting in xml for the user to use this xmll – Programmerzzz Mar 28 '18 at 04:15
  • @Dweeberly As you can see in my target output XML format, not all attributes are on new lines, I want only some of them in new lines which I can control as I did in my code. but my prob is all that formatting is lost once I use the `XElement.Parse` Method.. – Programmerzzz Mar 28 '18 at 05:01
  • The system is broken and doesn't comply with xml specification. Xml spec ignores white spaces when reading a file. i would look at fixing the reading function, not try to patch the create method. – jdweng Mar 28 '18 at 06:52
  • @jdweng i get you but like i said customer dont have control on tool to which thia xml is fed. Its a third party tool – Programmerzzz Mar 28 '18 at 13:58
  • Make sure from vendor you have latest version of his tools. Xml doesn't require the weird formatting that is being used. – jdweng Mar 28 '18 at 19:37

1 Answers1

0

You should take a look at this question: xdocument save preserve white space inside tags

LoadOptions.PreserveWhitespace (LoadOptions Enum)

If you preserve white space when loading, all insignificant white space in the XML tree is materialized in the XML tree as is. If you do not preserve white space, then all insignificant white space is discarded.

This gives the impression that 'insignificant' whitespace between attributes would be preserved.

If however you look at XElement.Parse Method you see this:

If the source XML is indented, setting the PreserveWhitespace flag in options causes the reader to read all white space in the source XML. Nodes of type XText are created for both significant and insignificant white space.

Looking at the class hierarchy you can see that XAttribute does not inherit from XNode. The long and the short of that is whitespace between attributes are not preserved. If they were you would still have to disable formatting on output (something like ToString(SaveOptions.DisableFormatting)).

I don't think that attributes were designed to be used as you have, but it is a very common usage. There is considerable diversity of opinion about this (see: Attribute vs Element)

Either way it sounds like you are stuck with both the design and format of what you were given. Unfortunately, this means you are also stuck with having to create a custom formatter to get the output you need.

Note the following code is meant only as an example of one possible way to implement code that creates the format you ask about.

using System;
using System.Linq;
using System.Text;
using System.Xml.Linq;

namespace FormatXml {
    class Program {
        static String OutputElement(int indentCnt, XElement ele) {
            StringBuilder sb = new StringBuilder();

            var indent = "".PadLeft(indentCnt, '\t');
            var specialFormat = (ele.Parent == null) ? false : ((ele.Parent.Name == "Name") ? true : false);
            sb.Append($"{indent}<{ele.Name}");

            String FormatAttr(XAttribute attr) {
                return $"{attr.Name} = '{attr.Value}'";
                }

            String FormatAttrByName(String name) {
                var attr = ele.Attributes().Where(x => x.Name == name).FirstOrDefault();
                var rv = "";
                if (attr == null) {
                    rv = $"{name}=''";
                    }
                else {
                    rv = FormatAttr(attr);
                    }
                return rv;
                }

            if (specialFormat) {
                var dob       = FormatAttrByName("DOB");
                var firstName = FormatAttrByName("FirstName");
                var lastName  = FormatAttrByName("LastName");
                var address   = FormatAttrByName("Address");
                var phoneNum  = FormatAttrByName("PhoneNum");
                var city      = FormatAttrByName("City");
                var state     = FormatAttrByName("State");
                var country   = FormatAttrByName("Country");
                sb.AppendLine($"{dob} {firstName} {lastName}");
                var left = ele.Name.LocalName.Length + 5;
                var fill = indent + "".PadLeft(left);
                sb.AppendLine($"{fill}{address}");
                sb.AppendLine($"{fill}{phoneNum}");
                sb.AppendLine($"{fill}{city}");
                sb.AppendLine($"{fill}{state}");
                sb.AppendLine($"{fill}{country} />");
                }
            else {
                foreach (var attr in ele.Attributes()) {
                    sb.AppendFormat(" {0}", FormatAttr(attr));
                    }
                }
            sb.AppendLine(">");

            foreach (var e in ele.Elements()) {
                sb.Append(OutputElement(indentCnt + 1, e));
                }
            sb.AppendLine($"{indent}</{ele.Name}>");
            return sb.ToString();
            }

        static void Main(string[] args) {
            var txtEle = @"
    <Details>
        <Name>
            <JohnDoe DOB = '10' FirstName = '20' LastName = '40'
                         Address = '50'
                         PhoneNum = '60'
                         City = '70'
                         State = '80'
                         Country = '90' />
        </Name>
    </Details>";

            var plib = new XElement("PersonLib");
            XDocument xdoc = new XDocument(plib);
            var nameEle = XElement.Parse(txtEle, LoadOptions.PreserveWhitespace);
            xdoc.Root.Add(nameEle);

            var xml = OutputElement(0, (XElement)xdoc.Root);

            Console.WriteLine(xml);
            }
        }
    }
Dweeberly
  • 4,668
  • 2
  • 22
  • 41