XmlSerializer: 删除不必要的 xsi 和 xsd 名称空间

有没有办法配置 XmlSerializer,使其不在根元素中写入默认名称空间?

我得到的是:

<?xml ...>
<rootelement xmlns:xsi="..." xmlns:xsd="...">
</rootelement>

我想删除两个 xmlns 声明。

重复 : < a href = “ https://stackoverflow./questions/258960”> 如何在不获得 xmlns = “ ...”的情况下将对象序列化为 XML?

120702 次浏览
//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)

还有一个备选方案——您可以在要序列化的类型中提供 XmlSerializerNamespaces类型的成员。用 XmlNamespaceDeclarations属性装饰它。向该成员添加命名空间前缀和 URI。然后,任何没有显式提供 XmlSerializerNamespaces 的序列化都将使用已放入类型中的命名空间前缀 + URI 对。

示例代码,假设这是您的类型:

[XmlRoot(Namespace = "urn:mycompany.2009")]
public class Person {
[XmlAttribute]
public bool Known;
[XmlElement]
public string Name;
[XmlNamespaceDeclarations]
public XmlSerializerNamespaces xmlns;
}

你可以这样做:

var p = new Person
{
Name = "Charley",
Known = false,
xmlns = new XmlSerializerNamespaces()
}
p.xmlns.Add("",""); // default namespace is emoty
p.xmlns.Add("c", "urn:mycompany.2009");

这就意味着,任何没有指定自己的前缀 + URI 对的实例序列化都将使用“ urn: mycompany. 2009”名称空间的“ p”前缀。它还将省略 xsi 和 xsd 名称空间。

这里的区别在于,您将 XmlSerializerNamespaces 添加到类型本身,而不是在调用 XmlSerializer 时显式使用它。Serialize ().这意味着,如果您的类型的实例被您不拥有的代码序列化(例如在 webservices 堆栈中) ,并且该代码没有显式提供 XmlSerializerNamespaces,那么序列化器将使用实例中提供的名称空间。

自从戴夫要求我重复我的答案 在.NET 中序列化对象时省略所有 xsi 和 xsd 命名空间,我已经更新了这篇文章,并重复我的答案在这里从上述链接。这个答案中使用的例子与另一个问题中使用的例子相同。以下内容一字不差地照抄下来。


在网上阅读了微软的文档和一些解决方案之后,我发现了这个问题的解决方案。它可以通过 IXmlSerialiazble使用内置的 XmlSerializer和自定义的 XML 序列化。

为此,我将使用到目前为止在这个问题的答案中使用过的相同 MyTypeWithNamespaces XML 示例。

[XmlRoot("MyTypeWithNamespaces", Namespace="urn:Abracadabra", IsNullable=false)]
public class MyTypeWithNamespaces
{
// As noted below, per Microsoft's documentation, if the class exposes a public
// member of type XmlSerializerNamespaces decorated with the
// XmlNamespacesDeclarationAttribute, then the XmlSerializer will utilize those
// namespaces during serialization.
public MyTypeWithNamespaces( )
{
this._namespaces = new XmlSerializerNamespaces(new XmlQualifiedName[] {
// Don't do this!! Microsoft's documentation explicitly says it's not supported.
// It doesn't throw any exceptions, but in my testing, it didn't always work.


// new XmlQualifiedName(string.Empty, string.Empty),  // And don't do this:
// new XmlQualifiedName("", "")


// DO THIS:
new XmlQualifiedName(string.Empty, "urn:Abracadabra") // Default Namespace
// Add any other namespaces, with prefixes, here.
});
}


// If you have other constructors, make sure to call the default constructor.
public MyTypeWithNamespaces(string label, int epoch) : this( )
{
this._label = label;
this._epoch = epoch;
}


// An element with a declared namespace different than the namespace
// of the enclosing type.
[XmlElement(Namespace="urn:Whoohoo")]
public string Label
{
get { return this._label; }
set { this._label = value; }
}
private string _label;


// An element whose tag will be the same name as the property name.
// Also, this element will inherit the namespace of the enclosing type.
public int Epoch
{
get { return this._epoch; }
set { this._epoch = value; }
}
private int _epoch;


// Per Microsoft's documentation, you can add some public member that
// returns a XmlSerializerNamespaces object. They use a public field,
// but that's sloppy. So I'll use a private backed-field with a public
// getter property. Also, per the documentation, for this to work with
// the XmlSerializer, decorate it with the XmlNamespaceDeclarations
// attribute.
[XmlNamespaceDeclarations]
public XmlSerializerNamespaces Namespaces
{
get { return this._namespaces; }
}
private XmlSerializerNamespaces _namespaces;
}

这节课就到这里。现在,一些人反对在他们的类中的某个地方使用 XmlSerializerNamespaces对象,但是正如你所看到的,我把它巧妙地藏在了缺省构造函数中,并公开了一个公共属性来返回名称空间。

现在,当需要序列化该类时,您将使用以下代码:

MyTypeWithNamespaces myType = new MyTypeWithNamespaces("myLabel", 42);


/******
OK, I just figured I could do this to make the code shorter, so I commented out the
below and replaced it with what follows:


// You have to use this constructor in order for the root element to have the right namespaces.
// If you need to do custom serialization of inner objects, you can use a shortened constructor.
XmlSerializer xs = new XmlSerializer(typeof(MyTypeWithNamespaces), new XmlAttributeOverrides(),
new Type[]{}, new XmlRootAttribute("MyTypeWithNamespaces"), "urn:Abracadabra");


******/


/*****
Per @dbc, since MyTypeWithNamespaces has a XmlRootAttribute decorating the class,
You may be able to get away with NOT using this .ctor and use the simple
XmlSerializer(Type) .ctor.
Also, be careful not to use serializer creation in loops, as it could lead
to extensive memory issues due to how serializers are cached (or not...).
See @dbc's comment and link to SO Q&A for more details.


XmlSerializer xs = new XmlSerializer(typeof(MyTypeWithNamespaces),
new XmlRootAttribute("MyTypeWithNamespaces") { Namespace="urn:Abracadabra" });
****/
XmlSerializer xs = new XmlSerializer(typeof(MyTypeWithNamespaces));


// I'll use a MemoryStream as my backing store.
MemoryStream ms = new MemoryStream();


// This is extra! If you want to change the settings for the XmlSerializer, you have to create
// a separate XmlWriterSettings object and use the XmlTextWriter.Create(...) factory method.
// So, in this case, I want to omit the XML declaration.
XmlWriterSettings xws = new XmlWriterSettings();
xws.OmitXmlDeclaration = true;
xws.Encoding = Encoding.UTF8; // This is probably the default
// You could use the XmlWriterSetting to set indenting and new line options, but the
// XmlTextWriter class has a much easier method to accomplish that.


// The factory method returns a XmlWriter, not a XmlTextWriter, so cast it.
XmlTextWriter xtw = (XmlTextWriter)XmlTextWriter.Create(ms, xws);
// Then we can set our indenting options (this is, of course, optional).
xtw.Formatting = Formatting.Indented;


// Now serialize our object.
xs.Serialize(xtw, myType, myType.Namespaces);

完成此操作后,应该会得到以下输出:

<MyTypeWithNamespaces>
<Label xmlns="urn:Whoohoo">myLabel</Label>
<Epoch>42</Epoch>
</MyTypeWithNamespaces>

我在最近的一个项目中成功地使用了这种方法,该项目具有深层次的类,这些类被序列化为 XML 用于 Web 服务调用。微软的文档并不十分清楚,一旦创建了公开可访问的 XmlSerializerNamespaces成员,该如何处理它,因此许多人认为它是无用的。但是通过遵循他们的文档并按照上面所示的方式使用它,您可以自定义 XmlSerializer 如何为您的类生成 XML,而不必采用不受支持的行为或通过实现 IXmlSerializable“滚动您自己的”序列化。

我希望这个答案能够彻底解决如何摆脱由 XmlSerializer生成的标准 xsixsd名称空间的问题。

更新: 我只是想确保我回答了 OP 关于删除所有名称空间的问题。我上面的代码将为此工作; 让我向您展示如何工作。现在,在上面的示例中,您实际上不能去掉所有的名称空间(因为有两个正在使用的名称空间)。在 XML 文档的某个地方,您需要使用类似 xmlns="urn:Abracadabra" xmlns:w="urn:Whoohoo的东西。如果示例中的类是较大文档的一部分,那么必须为 AbracadbraWhoohoo中的一个(或两个)声明名称空间之上的某个位置。如果没有,那么其中一个或两个名称空间中的元素必须使用某种类型的前缀进行装饰(不能有两个默认名称空间,对吗?).因此,对于这个示例,Abracadabra是默认名称空间。我可以在我的 MyTypeWithNamespaces类中为 Whoohoo名称空间添加一个名称空间前缀,如下所示:

public MyTypeWithNamespaces
{
this._namespaces = new XmlSerializerNamespaces(new XmlQualifiedName[] {
new XmlQualifiedName(string.Empty, "urn:Abracadabra"), // Default Namespace
new XmlQualifiedName("w", "urn:Whoohoo")
});
}

现在,在我的类定义中,我指出 <Label/>元素位于名称空间 "urn:Whoohoo"中,因此我不需要做任何进一步的工作。当我现在使用上述序列化代码序列化该类时,输出结果如下:

<MyTypeWithNamespaces xmlns:w="urn:Whoohoo">
<w:Label>myLabel</w:Label>
<Epoch>42</Epoch>
</MyTypeWithNamespaces>

因为 <Label>与文档的其余部分位于不同的名称空间中,所以它必须以某种方式用名称空间“装饰”。注意,仍然没有 xsixsd名称空间。


这就是我对另一个问题的回答。但是我想确保回答了 OP 关于不使用名称空间的问题,因为我觉得我还没有真正解决这个问题。假设 <Label>与文档的其余部分属于同一名称空间,在本例中为 urn:Abracadabra:

<MyTypeWithNamespaces>
<Label>myLabel<Label>
<Epoch>42</Epoch>
</MyTypeWithNamespaces>

您的构造函数将与我的第一个代码示例中的构造函数一样,以及检索默认名称空间的 public 属性:

// As noted below, per Microsoft's documentation, if the class exposes a public
// member of type XmlSerializerNamespaces decorated with the
// XmlNamespacesDeclarationAttribute, then the XmlSerializer will utilize those
// namespaces during serialization.
public MyTypeWithNamespaces( )
{
this._namespaces = new XmlSerializerNamespaces(new XmlQualifiedName[] {
new XmlQualifiedName(string.Empty, "urn:Abracadabra") // Default Namespace
});
}


[XmlNamespaceDeclarations]
public XmlSerializerNamespaces Namespaces
{
get { return this._namespaces; }
}
private XmlSerializerNamespaces _namespaces;

之后,在使用 MyTypeWithNamespaces对象序列化它的代码中,您可以像我上面所做的那样调用它:

MyTypeWithNamespaces myType = new MyTypeWithNamespaces("myLabel", 42);


XmlSerializer xs = new XmlSerializer(typeof(MyTypeWithNamespaces),
new XmlRootAttribute("MyTypeWithNamespaces") { Namespace="urn:Abracadabra" });


...


// Above, you'd setup your XmlTextWriter.


// Now serialize our object.
xs.Serialize(xtw, myType, myType.Namespaces);

而且 XmlSerializer将生成与上面所示相同的 XML,输出中没有其他名称空间:

<MyTypeWithNamespaces>
<Label>myLabel<Label>
<Epoch>42</Epoch>
</MyTypeWithNamespaces>

我在用:

public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}


class Program
{
static void Main(string[] args)
{
const string DEFAULT_NAMESPACE = "http://www.something.org/schema";
var serializer = new XmlSerializer(typeof(Person), DEFAULT_NAMESPACE);
var namespaces = new XmlSerializerNamespaces();
namespaces.Add("", DEFAULT_NAMESPACE);


using (var stream = new MemoryStream())
{
var someone = new Person
{
FirstName = "Donald",
LastName = "Duck"
};
serializer.Serialize(stream, someone, namespaces);
stream.Position = 0;
using (var reader = new StreamReader(stream))
{
Console.WriteLine(reader.ReadToEnd());
}
}
}
}

获取以下 XML:

<?xml version="1.0"?>
<Person xmlns="http://www.something.org/schema">
<FirstName>Donald</FirstName>
<LastName>Duck</LastName>
</Person>

如果不需要名称空间,只需将 DEFAULT _ NAMESPACE 设置为“”。

我知道这是肮脏的,但它为我做的诀窍,只是使用正则表达式摆脱垃圾。我只是不想要任何 xmlns 的东西,想要像对待普通的 JSON 一样对待 XML。其他的答案太过仪式化了。

所以在序列化对象之后,我要:

string xml = "<string xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">Hello, world!</string>";
xml = Regex.Replace(x, @" xmlns:.*?"".*?""", "");