views:

2309

answers:

8

Hi,

I'm trying to generate XML like this:

<?xml version="1.0"?>
<!DOCTYPE APIRequest SYSTEM
"https://url"&gt;
<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""&gt;");
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"&gt;
<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 root element.

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.

+1  A: 

try DataContractSerializer.

ArsenMkrt
Thanks, any ideas for .NET 2.0 though?
Graphain
@ArsenMkrt: how would that have helped? -1.
John Saunders
@John DataContractSeializer doesn't create header <?xml..., so he could serialize each object by this class
ArsenMkrt
@ArsenMkrt: `DataContractSerializer` also doesn't emit DTD declarations.
John Saunders
+14  A: 

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

Output:

<?xml version="1.0" encoding="utf-16"?>
<!DOCTYPE APIRequest SYSTEM "https://url"&gt;
<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; }
}
Simon Svensson
Thanks, I knew the string concat was bad but I thought it would at least work - I guess not! Thanks :-)
Graphain
Just checked - thanks heaps!
Graphain
If I could vote this up more/give a bounty tip easily I would :-)
Graphain
A: 

This link: http://www.eggheadcafe.com/conversation.aspx?messageid=31106520&amp;threadid=31106516 talks about the same issue with the best conlusion being manual removal of the header so better solutions are welcome!

Graphain
A: 

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)
Doug D
@Doug D: I have trouble believing you consider this to be a better solution.
John Saunders
A: 

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.

Tone
@Tone: kind of a meee-tooo? It's just what Doug D said a month ago. -1.
John Saunders
A: 

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

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

Not elegant, but concise.

-1: how does it help?
John Saunders
A: 

The two-line answer is: use an XmlWriterSettings object with the OmitXmlDeclaration property to True -- pass the settings object as the second parameter when you create your XmlWriter.

tswaters
-1: Nonsense. Read the question.
John Saunders
+2  A: 

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;
}

Giuseppe Dimauro

Giuseppe Dimauro