22

I'm trying to generate XML like this:

<?xml version="1.0"?>
<!DOCTYPE APIRequest SYSTEM
"https://url">
<APIRequest>
  <Head>
      <Key>123</Key>
  </Head>
  <ObjectClass>
    <Field>Value</Field
  </ObjectClass>
</APIRequest>

I have a class (ObjectClass) decorated with XMLSerialization attributes like this:

[XmlRoot("ObjectClass")]
public class ObjectClass
{
    [XmlElement("Field")]
    public string Field { get; set; }
}

And my really hacky intuitive thought to just get this working is to do this when I serialize:

ObjectClass inst = new ObjectClass();
XmlSerializer serializer = new XmlSerializer(inst.GetType(), "");

StringWriter w = new StringWriter();
w.WriteLine(@"<?xml version=""1.0""?>");
w.WriteLine("<!DOCTYPE APIRequest SYSTEM");
w.WriteLine(@"""https://url"">");
w.WriteLine("<APIRequest>");
w.WriteLine("<Head>");
w.WriteLine(@"<Field>Value</Field>");
w.WriteLine(@"</Head>");

XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", ""); 
serializer.Serialize(w, inst, ns);

w.WriteLine("</APIRequest>");

However, this generates XML like this:

<?xml version="1.0"?>
<!DOCTYPE APIRequest SYSTEM
"https://url">
<APIRequest>
  <Head>
      <Key>123</Key>
  </Head>
  <?xml version="1.0" encoding="utf-16"?>
  <ObjectClass>
    <Field>Value</Field>
  </ObjectClass>
</APIRequest>

i.e. the serialize statement is automatically adding a <?xml text declaration.

I know I'm attacking this wrong so can someone point me in the right direction?

As a note, I don't think it will make practical sense to just make an APIRequest class with an ObjectClass in it (because there are say 20 different types of ObjectClass that each needs this boilerplate around them) but correct me if I'm wrong.

5
  • Thanks, any ideas for .NET 2.0 though? Commented Jun 1, 2009 at 5:43
  • Automatically? You seem to be adding the declaration manually: w.WriteLine(@"<?xml version=""1.0""?>");. Commented Jun 1, 2009 at 5:47
  • 1
    @Cerebrus, he does not want the inner <?xml?> inside the <APIRequest> tag. Commented Jun 1, 2009 at 5:55
  • @John DataContractSeializer doesn't create header <?xml..., so he could serialize each object by this class Commented Jul 31, 2009 at 3:35
  • DataContractSerializer also doesn't emit DTD declarations. Commented May 28, 2010 at 2:02

7 Answers 7

36

try this:

internal static string ToXml(object obj)
{
  string retval = null;
  if (obj != null)
  {
    StringBuilder sb = new StringBuilder();
    using(XmlWriter writer = XmlWriter.Create(sb, new XmlWriterSettings() { OmitXmlDeclaration = true }))
    {
      new XmlSerializer(obj.GetType()).Serialize(writer, obj);
    }
    retval = sb.ToString();
  }
  return retval;
}
Sign up to request clarification or add additional context in comments.

Comments

25

Never build xml using string concatenation. It's evil.

Output:

<?xml version="1.0" encoding="utf-16"?>
<!DOCTYPE APIRequest SYSTEM "https://url">
<APIRequest>
  <Head>
    <Key>123</Key>
  </Head>
  <ObjectClass>
    <Field>Value</Field>
  </ObjectClass>
</APIRequest>

Code:

using System;
using System.Diagnostics;
using System.Text;
using System.Xml;
using System.Xml.Serialization;

public static class Program {
    public static void Main() {
        var obj = new ObjectClass { Field = "Value" };

        var settings = new XmlWriterSettings {
            Indent = true
        };

        var xml = new StringBuilder();
        using (var writer = XmlWriter.Create(xml, settings)) {
            Debug.Assert(writer != null);

            writer.WriteDocType("APIRequest", null, "https://url", null);
            writer.WriteStartElement("APIRequest");
            writer.WriteStartElement("Head");
            writer.WriteElementString("Key", "123");
            writer.WriteEndElement(); // </Head>

            var nsSerializer = new XmlSerializerNamespaces();
            nsSerializer.Add("", "");

            var xmlSerializer = new XmlSerializer(obj.GetType(), "");
            xmlSerializer.Serialize(writer, obj, nsSerializer);

            writer.WriteEndElement(); // </APIRequest>
        }

        Console.WriteLine(xml.ToString());
        Console.ReadLine();
    }
}

[XmlRoot("ObjectClass")]
public class ObjectClass {
    [XmlElement("Field")]
    public string Field { get; set; }
}

2 Comments

Thanks, I knew the string concat was bad but I thought it would at least work - I guess not! Thanks :-)
I wouldn't say it's evil. It's not preferred, but if you have situations where performance is crucial and you've identified the writer as the source of that performance hit, then using string concatenation can be a tremendous win. Of course, it is situational, and I'd almost always use XmlWriter, but in some cases, it is justified.
2

If you don't want to rely on an xml writer for performance reasons etc you can do this:

// Read into memory stream and set namespaces to empty strings
XmlSerializerNamespaces nsSerializer = new XmlSerializerNamespaces();
nsSerializer.Add(string.Empty, string.Empty);
XmlSerializer xs = new XmlSerializer(typeof(Model.AudioItem));
xs.Serialize(ms, item, nsSerializer);

// Read into UTF-8 stream and read off first line (i.e "<?xml version="1.0"?>")
StreamReader sr = new StreamReader(ms);
ms.Position = 0;
sr.ReadLine();

sr.ReadToEnd().ToString() now contains the naked serialization

1 Comment

feels dirty, and yet clever, for the ReadLine trick. I like it. Any concerns with indenting and such?
1

Derive your own XmlTextWriter to omit the XML declaration.

Private Class MyXmlTextWriter
Inherits XmlTextWriter
Sub New(ByVal sb As StringBuilder)
    MyBase.New(New StringWriter(sb))
End Sub
Sub New(ByVal w As TextWriter)
    MyBase.New(w)
End Sub

Public Overrides Sub WriteStartDocument()
    ' Don't emit XML declaration
End Sub
Public Overrides Sub WriteStartDocument(ByVal standalone As Boolean)
    ' Don't emit XML declaration
End Sub
End Class

Call Serialize with an instance of the derived MyXmlTextWriter.

Dim tw As New MyXmlTextWriter(sb)
Dim objXmlSerializer As New XmlSerializer(type)
objXmlSerializer.Serialize(tw, obj)

1 Comment

@Doug D: I have trouble believing you consider this to be a better solution.
1

Scott Hanselman's got a good post on this. I used Kzu's example (which Scott's blog points to) a while back for the same thing and it worked great.

1 Comment

@Tone: kind of a meee-tooo? It's just what Doug D said a month ago. -1.
-1
if (!string.IsNullOrEmpty(strXML) && strXML.Contains(@"<?xml"))
strXML = strXML.Remove(0, strXML.IndexOf(@"?>", 0) + 2);

2 Comments

strXML = strXML.Remove(0, sXMLContent.IndexOf(@"?>", 0) + 2);
can you please explain your answer?
-2

One liner, to remove the first line from a string:

String.Join("\n", strXML.Split('\n').Skip(1).ToArray())

Not elegant, but concise.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.