0

I'm building an MVC5 application which pulls records from a database and allows a user to perform some basic data cleansing edits.

Once the data has been cleansed it needs to be exported as XML, run through a validator and then uploaded to a third party portal.

I'm using Service Stack, and I've found it fairly quick and straightforward in the past, particularly when outputting to CSV.

The one issue I'm having is with the XML serialzer. I'm not sure how to make it generate well formed XML.

The file that i'm getting simply dumps it on one line, which won't validate because it isn't well formed.

below is an extract from my controller action:

 Response.Clear();
 Response.ContentType = "text/xml";
 Response.AddHeader("Content-Disposition", "attachment; filename="myFile.xml"");
 XmlSerializer.SerializeToStream(viewModel, Response.OutputStream);
 Response.End();

UPDATE: Thanks for the useful comments, as explained I'm not talking about pretty printing, the issue is I need to run the file through a validator before uploading it to a third party. The error message the validator is throwing is Error:0000, XML not well-formed. Cannot have more than one tag on one line.

tereško
  • 58,060
  • 25
  • 98
  • 150
  • Most white space (including new lines) in XML is *insignificant* -- it has no meaning, and is only for beautification. The lack of white space doesn't make the XML ill-formed. See http://usingxml.com/Basics/XmlSpace or https://www.w3.org/TR/REC-xml/#sec-white-space. – dbc Feb 08 '16 at 17:00
  • 1
    Can you provide an example when serializer returns non [Well formed XML](https://en.wikipedia.org/wiki/Well-formed_document)? (I suspect that you are asking about "pretty printing" since "well formed" in XML puts no requirements on extra new lines, but post explicitly highlights "well formed", so I could be wrong) – Alexei Levenkov Feb 08 '16 at 17:20
  • Have you tried `viewModel.ToXml()`? Otherwise please provide the class definition of `viewModel` you're trying to serialize along with the serialized XML. – mythz Feb 08 '16 at 18:11
  • @AlexeiLevenkov - it wouldn't be appropriate to post an example as I'm working with personal information. I need to produce XML that can be validated according to what their definition of well formed is, and it looks like they want one tag on one line. I applaud you use of apostrophes and your "helpful" comment based on what you suspect I was asking, rather than what I was actually asking, "thanks" –  Feb 09 '16 at 09:18
  • @D.Mac I'm sorry you've found my comment to be so offensive. I've used quotes to quote names you may be interested in, but I see how one can consider them as used to inverse the meaning. – Alexei Levenkov Feb 09 '16 at 16:57

1 Answers1

2

Firstly, be aware that most white space (including new lines) in XML is insignificant -- it has no meaning, and is only for beautification. The lack of new lines doesn't make the XML ill-formed. See White Space in XML Documents or https://www.w3.org/TR/REC-xml/#sec-white-space. Thus in theory it shouldn't matter whether ServiceStack's XmlSerializer is putting all of your XML on a single line.

That being said, if for whatever reason you must cosmetically break your XML up into multiple lines, you'll need to do a little work. From the source code we can see that XmlSerializer uses DataContractSerializer with a hardcoded static XmlWriterSettings that does not allow for setting XmlWriterSettings.Indent = true. However, since this class is just a very thin wrapper on Microsoft's data contract serializer, you can substitute your own code:

public static class DataContractSerializerHelper
{
    private static readonly XmlWriterSettings xmlWriterSettings = new XmlWriterSettings { Indent = true, IndentChars = "  " };

    public static string SerializeToString<T>(T from)
    {
        try
        {
            using (var ms = new MemoryStream())
            using (var xw = XmlWriter.Create(ms, xmlWriterSettings))
            {
                var serializer = new DataContractSerializer(from.GetType());
                serializer.WriteObject(xw, from);
                xw.Flush();
                ms.Seek(0, SeekOrigin.Begin);
                var reader = new StreamReader(ms);
                return reader.ReadToEnd();
            }
        }
        catch (Exception ex)
        {
            throw new SerializationException(string.Format("Error serializing \"{0}\"", from), ex);
        }
    }

    public static void SerializeToWriter<T>(T value, TextWriter writer)
    {
        try
        {
            using (var xw = XmlWriter.Create(writer, xmlWriterSettings))
            {
                var serializer = new DataContractSerializer(value.GetType());
                serializer.WriteObject(xw, value);
            }
        }
        catch (Exception ex)
        {
            throw new SerializationException(string.Format("Error serializing \"{0}\"", value), ex);
        }
    }

    public static void SerializeToStream(object obj, Stream stream)
    {
        if (obj == null) 
            return;
        using (var xw = XmlWriter.Create(stream, xmlWriterSettings))
        {
            var serializer = new DataContractSerializer(obj.GetType());
            serializer.WriteObject(xw, obj);
        }
    }
}

And then do:

DataContractSerializerHelper.SerializeToStream(viewModel, Response.OutputStream);
dbc
  • 104,963
  • 20
  • 228
  • 340