如何将一个对象序列化为XML,而不获取xmlns="..."?

123

在.NET中,有没有一种方法可以将对象序列化而不自动序列化XML名称空间?似乎默认情况下,.NET认为应该包括XSI和XSD命名空间,但我不想要它们。

6个回答

163

啊...算了。通常是在提出问题后搜索才能得出答案。我正在序列化的对象是obj,已经被定义。向集合中添加一个带有单个空命名空间的XMLSerializerNamespace即可解决问题。

在VB中如下:

Dim xs As New XmlSerializer(GetType(cEmploymentDetail))
Dim ns As New XmlSerializerNamespaces()
ns.Add("", "")

Dim settings As New XmlWriterSettings()
settings.OmitXmlDeclaration = True

Using ms As New MemoryStream(), _
    sw As XmlWriter = XmlWriter.Create(ms, settings), _
    sr As New StreamReader(ms)
    xs.Serialize(sw, obj, ns)
    ms.Position = 0
    Console.WriteLine(sr.ReadToEnd())
End Using

在 C# 中像这样:

//Create our own namespaces for the output
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();

//Add an empty namespace and empty value
ns.Add("", "");

//Create the serializer
XmlSerializer slz = new XmlSerializer(someType);

//Serialize the object with our own namespaces (notice the overload)
slz.Serialize(myXmlTextWriter, someObject, ns);

15
我尝试在VB中做了这个,xsi和xsd属性消失了,但是出现了xmlns:q12=、d3p1:type和xmlns:d3p1等属性。 - MiddleKay
20
我尝试了C#版本,它删除了xsi和xsd,但为所有XML标签名称添加了q1:前缀,这不是我想要的。看起来C#示例是不完整的,引用了myXmlTextWriter,我认为需要像VB示例那样进行初始化。 - redtetrahedron
4
你是否找到了摆脱“q1垃圾”的方法? - crush
1
如果将q1添加为空名称空间,请参考答案https://dev59.com/kI3da4cB1Zd3GeqP7Pkf。 - aniruddha

22

如果你想要去掉额外的xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:xsd="http://www.w3.org/2001/XMLSchema" ,但仍然保留自己的命名空间 xmlns="http://schemas.YourCompany.com/YourSchema/",只需要做出如下简单的修改:

//  Add lib namespace with empty prefix  
ns.Add("", "http://schemas.YourCompany.com/YourSchema/");   

18
如果您想删除命名空间,可能还希望删除版本号,为了节省您的搜索时间,我已经添加了该功能,因此下面的代码将同时执行这两个操作。
我还将其包装在通用方法中,因为我正在创建非常大的XML文件,这些文件太大而无法在内存中序列化,因此我已将输出文件拆分并将其序列化为较小的“块”。
    public static string XmlSerialize<T>(T entity) where T : class
    {
        // removes version
        XmlWriterSettings settings = new XmlWriterSettings();
        settings.OmitXmlDeclaration = true;

        XmlSerializer xsSubmit = new XmlSerializer(typeof(T));
        using (StringWriter sw = new StringWriter())
        using (XmlWriter writer = XmlWriter.Create(sw, settings))
        {
            // removes namespace
            var xmlns = new XmlSerializerNamespaces();
            xmlns.Add(string.Empty, string.Empty);

            xsSubmit.Serialize(writer, entity, xmlns);
            return sw.ToString(); // Your XML
        }
    }

注意,StringWriter 默认使用 UTF-16 编码,这可能会导致下游反序列化问题。 using (var reader = XmlReader.Create(stream)){ reader.Read(); } 这会抛出一个异常,因为声明是 UTF-16,而实际内容是以 UTF-8 写入的。 System.Xml.XmlException: 'There is no Unicode byte order mark. Cannot switch to Unicode.' - Tyler StandishMan
为了解决这个问题并仍然使用 XmlReader,您可以使用 var streamReader = new StreamReader(stream, System.Text.Encoding.UTF8, true);。如果找到 BOM,则 true 将使用它,否则使用您提供的默认值。 - Tyler StandishMan
改成:(这个 T 实体)现在你有一个很棒的扩展方法。 - undefined

10
我建议使用这个辅助类:
public static class Xml
{
    #region Fields

    private static readonly XmlWriterSettings WriterSettings = new XmlWriterSettings {OmitXmlDeclaration = true, Indent = true};
    private static readonly XmlSerializerNamespaces Namespaces = new XmlSerializerNamespaces(new[] {new XmlQualifiedName("", "")});

    #endregion

    #region Methods

    public static string Serialize(object obj)
    {
        if (obj == null)
        {
            return null;
        }

        return DoSerialize(obj);
    }

    private static string DoSerialize(object obj)
    {
        using (var ms = new MemoryStream())
        using (var writer = XmlWriter.Create(ms, WriterSettings))
        {
            var serializer = new XmlSerializer(obj.GetType());
            serializer.Serialize(writer, obj, Namespaces);
            return Encoding.UTF8.GetString(ms.ToArray());
        }
    }

    public static T Deserialize<T>(string data)
        where T : class
    {
        if (string.IsNullOrEmpty(data))
        {
            return null;
        }

        return DoDeserialize<T>(data);
    }

    private static T DoDeserialize<T>(string data) where T : class
    {
        using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(data)))
        {
            var serializer = new XmlSerializer(typeof (T));
            return (T) serializer.Deserialize(ms);
        }
    }

    #endregion
}

:)


很棒的答案 :) 我也加了这一行 stream.Position = 0; 并在我的解决方案中返回了整个流.. 结果如预期 - 所有声明标签都被移除了。 - ymz
将命名空间参数添加到序列化器调用中就足以让我删除默认的命名空间。在我的看法中,编写new XmlSerializerNamespaces(new[] {XmlQualifiedName.Empty})而不是new XmlSerializerNamespaces(new[] {new XmlQualifiedName("", "")})是一种更清晰的编码方式。 - Suncat2000

7
如果你无法在从生成的类(例如使用xsd.exe)序列化为xml时,消除每个元素的额外xmlns属性,那么你可能会遇到以下情况:
<manyElementWith xmlns="urn:names:specification:schema:xsd:one" />

然后我会与您分享对我有效的内容(结合之前的答案和我在这里找到的内容:这里)。
明确设置所有不同的xmlns,如下所示:
Dim xmlns = New XmlSerializerNamespaces()
xmlns.Add("one", "urn:names:specification:schema:xsd:one")
xmlns.Add("two",  "urn:names:specification:schema:xsd:two")
xmlns.Add("three",  "urn:names:specification:schema:xsd:three")

然后将其传递给序列化程序。
serializer.Serialize(writer, object, xmlns);

在根元素中声明三个命名空间,并且不需要在其他元素中生成更多的命名空间,这些元素将相应地带有前缀。
<root xmlns:one="urn:names:specification:schema:xsd:one" ... />
   <one:Element />
   <two:ElementFromAnotherNameSpace /> ...

0
        XmlWriterSettings settings = new XmlWriterSettings
        {
            OmitXmlDeclaration = true
        };

        XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
        ns.Add("", "");

        StringBuilder sb = new StringBuilder();

        XmlSerializer xs = new XmlSerializer(typeof(BankingDetails));

        using (XmlWriter xw = XmlWriter.Create(sb, settings))
        {
            xs.Serialize(xw, model, ns);
            xw.Flush();
            return sb.ToString();
        }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接