0

I want to generate an XML of the following format using XmlWriter class in C# -:

<?xml version="1.0" ?>
<root>
 <data>
  <entry Attrib1="" Attrib2="91.3467" Attrib3="95.3052" Attrib4="6.4722" />
  <entry Attrib1="" Attrib2="91.3467" Attrib3="95.3052" Attrib4="6.4722" />
 </data>
</root>

I am very new to XmlWriter class and to C# in general and I have tried writing code for generating the file with the above format, but that attempt was unsuccessful

var xmlWriter = XmlWriter.Create(filename);
    xmlWriter.WriteStartDocument();
    xmlWriter.WriteStartElement("data");

    xmlWriter.WriteStartElement("entry");
    xmlWriter.WriteAttributeString("attrib1", "value1");
    xmlWriter.WriteAttributeString("attrib2", "value2");
    xmlWriter.Close();

also, the name of the attributes can included illegal XML characters and that's why I read up on XMLWriter because it seems to remove those illegal characters from the names of the attributes for instance a name like "this is attribute 1" should be reduced to something like "this_is_attribute_1" when written to the resulting XML, how do I go about producing such XML using XmlWriter. In short a row of the resulting XML is something like this

<entry P_B_Pe="" P_E_Pe="91.3467" Custom_Price="95.3052" C_Yield="6.4722" Average_Life="" />
Arebhy Sridaran
  • 586
  • 12
  • 28
AnkitSablok
  • 3,021
  • 7
  • 35
  • 52

3 Answers3

1

You've almost got it...

var xmlWriter = XmlWriter.Create(filename);
    xmlWriter.WriteStartDocument();
        xmlWriter.WriteStartElement("root");
            xmlWriter.WriteStartElement("data");
                xmlWriter.WriteStartElement("entry");
                    xmlWriter.WriteAttributeString("attrib1", "value1");
                    xmlWriter.WriteAttributeString("attrib2", "value2");
                xmlWriter.WriteEndElement(); // entry
                xmlWriter.WriteStartElement("entry");
                    xmlWriter.WriteAttributeString("attrib1", "value1");
                    xmlWriter.WriteAttributeString("attrib2", "value2");
                xmlWriter.WriteEndElement(); // entry
            xmlWriter.WriteEndElement(); // data
        xmlWriter.WriteEndElement(); // root
    xmlWriter.WriteEndDocument();
xmlWriter.Close();

By default XmlWriter will encode characters that are not normally valid in raw data or in attribute values in such a way that they will come back when you use a reader decode the XML, but attribute and element names must still be valid. If you want to handle invalid characters for those in some special way that's different from that, you'll need to do that yourself according to whatever rules you want to establish, something like:

xmlWriter.WriteAttributeString(MyXmlExtensions.EncodeXmlAttributeName("this is normally an invalid attribute name"), "value1");

class MyXmlExtensions
{
    public string EncodeXmlAttributeName(string decoded)
    {
        // not that you'll likely need to enhance this with whatever rules you want but haven't specified
        return decoded.Replace(" ", "_");
    }
    public string DecodeXmlAttributeName(string encoded)
    {
        // not that you'll likely need to enhance this with whatever rules you want but haven't specified
        return encoded.Replace("_", " ");
    }
}

You'll also need to use XmlWriterSettings in the call to XmlWriter.Create if you want the output to look pretty (tabs, multiple lines, etc.).

James
  • 3,551
  • 1
  • 28
  • 38
  • hey thanks for a solution like that, just wanted to know like is there nothing in .Net that converts an attribute string to a valid XML attribute name for instance if I have a string like Port. Entry is then the valid XML attribute should be Port_Entry_is . This is where I am facing most confusion, I looked at XmlConvert.Encode method as well but that also doesn't serve the purpose for me. – AnkitSablok Dec 11 '15 at 19:05
  • @AnkitSablok: that's correct. .NET doesn't support any special encoding for element and attribute names. `XmlConvert.Encode` is what is used internally to encode raw string data dumped into the element contents rather than their names or attributes. It can also be used by callers if they're generating XML manually and adding it using `XmlWriter.WriteRaw`. You could do something similar, but even `XmlConvert.Encode` wouldn't work for element names. – James Dec 11 '15 at 19:20
  • Cool, that is what I was looking for, so in the end can we safely conclude that the format in which attribute names are required is totally dependent on the situation/ context in which the XML is generated? – AnkitSablok Dec 11 '15 at 19:23
  • @AnkitSablok I'm not sure what you mean by that. The attribute name you specify in calls to `WriteAttributeString` must conform to XML standards for attribute names. If you want to coerce an attribute name that doesn't conform into one that does, that's completely up to you, and whether or not you can get back to what you put in is also up to you. You could make a function that replaces every invalid character with an underscore (not reversible), or you could come up with some kind of encoding that was reversible. – James Dec 11 '15 at 19:47
0

Try XML Linq

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.IO;

namespace ConsoleApplication1
{
    class Program
    {
        const string FILENAME = @"c:\temp\test.xml";
        static void Main(string[] args)
        {
            StreamWriter sWriter = new StreamWriter(FILENAME);
            XmlTextWriter writer = new XmlTextWriter(sWriter);

            writer.WriteStartDocument();
            writer.WriteStartElement("root");
            writer.WriteStartElement("data");

            double?[] attributes = new double?[] { null, 91.3467, 95.3052, 6.4722 };
            XElement entry = new XElement("entry");
            int index = 1;

            foreach (double? attribute in attributes)
            {
                if (attribute == null)
                {
                    entry.Add(new XAttribute("Attrib" + index++.ToString(), ""));
                }
                else
                {
                    entry.Add(new XAttribute("Attrib" + index++.ToString(), attribute));
                }
            }

            writer.WriteRaw(entry.ToString());
            writer.WriteRaw(entry.ToString());

            writer.WriteEndElement();
            writer.WriteEndElement();

            writer.Flush();
            writer.Close();


        }
    }
}
​

doing without any xml linq then

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.IO;

namespace ConsoleApplication1
{
    class Program
    {
        const string FILENAME = @"c:\temp\test.xml";
        static void Main(string[] args)
        {
            StreamWriter sWriter = new StreamWriter(FILENAME);
            XmlTextWriter writer = new XmlTextWriter(sWriter);

            writer.WriteStartDocument();
            writer.WriteStartElement("root");
            writer.WriteStartElement("data");
            writer.WriteStartElement("entry");

            double?[] attributes = new double?[] { null, 91.3467, 95.3052, 6.4722 };
            int index = 1;
            foreach (double? attribute in attributes)
            {
                writer.WriteStartAttribute("Attrib" + index++.ToString());
                if (attribute == null)
                {
                    writer.WriteValue("");
                }
                else
                {
                    writer.WriteValue(attribute);
                } 
                writer.WriteEndAttribute();

            }

            writer.WriteEndElement();

            writer.WriteStartElement("entry");
            attributes = new double?[] { null, 91.3467, 95.3052, 6.4722 };
            index = 1;
            foreach (double? attribute in attributes)
            {
                writer.WriteStartAttribute("Attrib" + index++.ToString());
                if (attribute == null)
                {
                    writer.WriteValue("");
                }
                else
                {
                    writer.WriteValue(attribute);
                }
                writer.WriteEndAttribute();

            }

            writer.WriteEndElement();

            writer.WriteEndElement();
            writer.WriteEndElement();

            writer.Flush();
            writer.Close();


        }
    }
}
​
jdweng
  • 33,250
  • 2
  • 15
  • 20
  • so, the situation is that I have the data coming in from a file or a stream as you like, and I want to use XmlTextWriter to write this data to a result XML, I have a solution that does the exact same thing you wrote that is using the XDocument class and XAttribute class but I don't want to use those constructs in the first place. – AnkitSablok Dec 11 '15 at 18:20
  • suppose the attributes of an XML entry are something like this "the name of the person is" , "the age of the person is", that is the attributes have special characters like spaces, &, ", , etc. and I want these attributes in their valid format when writing the resulting xml, as in in the result they should appear as follows without doing any string manipulation, is that possible using XmlTextWriter? – AnkitSablok Dec 11 '15 at 18:52
  • The new XAttribute(string1, string2) take two strings and automatically adds the equal sign. So no string manipulation is required. With XmlTextWriter the name and the value are two different lines of code as in my additional posted code. – jdweng Dec 11 '15 at 19:05
0

Use object serialisation, then you don't have to have object to structure mapping code

using System.Xml.Serialization;

...

[XmlRoot("root")]
public class Example {
    [XmlElement("data")]
    public Entries Entries { get; set; }
}

public class Entries : List<List<string>>, IXmlSerializable {

    public List<string> Attribs { get; set; }

    public System.Xml.Schema.XmlSchema GetSchema() { return null; }

    public void ReadXml(System.Xml.XmlReader reader) { reader.MoveToContent(); }

    public void WriteXml(System.Xml.XmlWriter writer) {
        foreach (var entry in this) {
            writer.WriteStartElement("entry", "");
            var label = 1;
            foreach (var attrib in entry) {
                writer.WriteAttributeString(string.Format("Attrib{0}", label), attrib);
                label++;
            }
            writer.WriteEndElement();
        }
    }
}

...

var xml = new XmlSerializer(typeof(Example), "");
xml.Serialize(stream, example);
TFD
  • 23,890
  • 2
  • 34
  • 51
  • This looks really clean, but the thing is the number of attributes is not predefined hence I can't take this approach, but this is a lot cleaner as to what I am doing currently. Thanks :) – AnkitSablok Dec 11 '15 at 21:56
  • @AnkitSablok Simple adjustment, just add custom serialiser class – TFD Dec 11 '15 at 23:18