代码之家  ›  专栏  ›  技术社区  ›  Matt Mitchell

不带<?的.NET XML序列化XML>文本声明

  •  20
  • Matt Mitchell  · 技术社区  · 15 年前

    我正在尝试生成这样的XML:

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

    我有一个类(objectclass),用如下的xmlserialization属性修饰:

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

    而我真正的黑客直觉是,当我序列化时,要做到这一点:

    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>");
    

    但是,这会生成这样的XML:

    <?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>
    

    即,序列化语句将自动添加<?XML文本声明。

    我知道我在攻击这个错误,所以有人能指出我的正确方向吗?

    作为一个注释,我认为仅仅让一个包含objectclass的apiRequest类(因为有20种不同类型的objectclass需要在它们周围使用这个样板)是没有实际意义的,但是如果我错了,请纠正我。

    7 回复  |  直到 9 年前
        1
  •  24
  •   sisve    15 年前

    从不使用字符串串联来构建XML。这是邪恶的。

    输出:

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

    代码:

    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
  •  30
  •   p.campbell    12 年前

    试试这个:

    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
  •  2
  •   nanobar    13 年前

    如果出于性能等原因不想依赖XML编写器,可以这样做:

    // 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()现在包含裸序列化

        4
  •  1
  •   Doug Domeny    15 年前

    派生自己的xmlTextWriter以省略XML声明。

    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
    

    使用派生MyXmlTextWriter的实例调用Serialize。

    Dim tw As New MyXmlTextWriter(sb)
    Dim objXmlSerializer As New XmlSerializer(type)
    objXmlSerializer.Serialize(tw, obj)
    
        5
  •  1
  •   Tone    15 年前

    Scott Hanselman's 在这方面有个好消息。我用了kzu的例子(斯科特的博客指出),一段时间前,同样的事情,它工作得很好。

        6
  •  -1
  •   CoderPi    9 年前
    if (!string.IsNullOrEmpty(strXML) && strXML.Contains(@"<?xml"))
    strXML = strXML.Remove(0, strXML.IndexOf(@"?>", 0) + 2);
    
        7
  •  -2
  •   JMax Dan    13 年前

    一个衬管,从管柱上移除第一行:

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

    不是优雅,而是简洁。