问题
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.
回答1:
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:
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;
}
回答3:
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
回答4:
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)
回答5:
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.
回答6:
if (!string.IsNullOrEmpty(strXML) && strXML.Contains(@"<?xml"))
strXML = strXML.Remove(0, strXML.IndexOf(@"?>", 0) + 2);
回答7:
One liner, to remove the first line from a string:
String.Join("\n", strXML.Split('\n').Skip(1).ToArray())
Not elegant, but concise.
来源:https://stackoverflow.com/questions/933664/net-xml-serialization-without-xml-text-declaration