有没有一种方法可以配置XmlSerializer,以使其不在根元素中写入默认名称空间?
我得到的是:
<?xml ...>
<rootelement xmlns:xsi="..." xmlns:xsd="...">
</rootelement>
而且我想删除两个xmlns声明。
有没有一种方法可以配置XmlSerializer,以使其不在根元素中写入默认名称空间?
我得到的是:
<?xml ...>
<rootelement xmlns:xsi="..." xmlns:xsd="...">
</rootelement>
而且我想删除两个xmlns声明。
Answers:
由于Dave要求我在序列化.NET中的对象时重复我的回答,即省略所有xsi和xsd命名空间,因此,我更新了这篇文章,并从上述链接在这里重复了我的回答。此答案中使用的示例与其他问题使用的示例相同。逐字复制以下内容。
在在线阅读Microsoft的文档和几种解决方案后,我发现了解决此问题的方法。它可以通过内置XmlSerializer
和自定义XML序列化一起使用IXmlSerialiazble
。
到目前为止,我将使用与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");
******/
XmlSerializer xs = new XmlSerializer(typeof(MyTypeWithNamespaces),
new XmlRootAttribute("MyTypeWithNamespaces") { Namespace="urn:Abracadabra" });
// 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
创建公开可访问成员后,Microsoft的文档尚不清楚如何处理它,因此许多人认为它没有用。但是通过遵循他们的文档并以上面显示的方式使用它,您可以自定义XmlSerializer如何为您的类生成XML,而无需诉诸不受支持的行为或通过实现实现“滚动自己的”序列化IXmlSerializable
。
我希望这个答案能够一劳永逸地解决如何摆脱产生的标准xsi
和xsd
名称空间XmlSerializer
。
更新:我只想确保我回答了OP有关删除所有名称空间的问题。我上面的代码将为此工作;让我告诉你怎么做。现在,在上面的示例中,您确实无法摆脱所有名称空间(因为有两个名称空间正在使用中)。在XML文档中的某处,您将需要具有xmlns="urn:Abracadabra" xmlns:w="urn:Whoohoo
。如果在示例类是一个更大的文档的一部分,然后某处命名空间上方必须声明为中的任一个(或两者)Abracadbra
和Whoohoo
。如果不是,则必须使用某种前缀来装饰一个或两个名称空间中的元素(不能有两个默认名称空间,对吗?)。因此,对于此示例,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>
它与文档的其余部分位于不同的名称空间中,所以在某种程度上,它必须用名称空间“修饰”。请注意,仍然没有xsi
和xsd
名称空间。
这结束了我对另一个问题的回答。但是我想确保我回答了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>
XmlTextWriter xtw = (XmlTextWriter)XmlTextWriter.Create(ms, xws);
我必须替换为var xtw = XmlTextWriter.Create(memStm, xws);
。
XmlTextWriter.Create
返回一个(抽象的?)XmlWriter
实例。因此,@ Preza8是正确的,您将失去设置其他XmlTextWriter
特定属性的能力(至少在没有向下转换的情况下),因此将特定的类型转换为XmlTextWriter
。
//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,则该序列化程序将使用实例中提供的名称空间。
我正在使用:
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设置为“”即可。