2013-11-27 52 views
0

我遇到以下问题。每当我在我的C#类中使用默认的XML序列化时,.NET序列化引擎会自动添加名称空间xsixsd。但是,当通过IXmlSerializable定义序列化时,不会添加名称空间。使用自定义IXmlSerializable时缺少xsi和xsd名称空间

实施例:此代码:

class Program 
{ 
    static void Main(string[] args) 
    { 
     OutputSerialized(new Outer() { Inner = new Inner() }); 
     OutputSerialized(new OuterCustom() { Inner = new Inner() }); 
    } 

    static void OutputSerialized<T>(T t) 
    { 
     var sb = new StringBuilder(); 
     using (var textwriter = new StringWriter(sb)) 
      new XmlSerializer(typeof(T)).Serialize(textwriter, t); 
     Console.WriteLine(sb.ToString()); 
    } 
} 

[Serializable] public class Inner { } 

[Serializable] public class Outer { public Inner Inner { get; set; } } 

public class OuterCustom : IXmlSerializable 
{ 
    public Inner Inner; 

    public void WriteXml(System.Xml.XmlWriter writer) 
    { 
     writer.WriteStartElement("Inner"); 
     new XmlSerializer(typeof(Inner)).Serialize(writer, Inner); 
     writer.WriteEndElement(); 
    } 

    public System.Xml.Schema.XmlSchema GetSchema() { return null; } 
    public void ReadXml(System.Xml.XmlReader reader) { /**/ } 
} 

产生以下输出:

<?xml version="1.0" encoding="utf-16"?> 
<Outer xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 
    <Inner /> 
</Outer> 
<?xml version="1.0" encoding="utf-16"?> 
<OuterCustom> 
    <Inner> 
    <Inner xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" /> 
    </Inner> 
</OuterCustom> 

你可以看到,OuterCustom的序列化形式缺少xsdxsi命名空间。

如何让我的OuterCustom的行为方式与Outer相同?我在代码中丢失了什么吗?我在做自定义序列化错误吗?

这里有对SO有关如何摆脱额外的命名空间的很多问题,但似乎没有人问如何让他们回来

+0

从XML的观点来看,2个XML样本没有区别...您是否试图对XML进行一些字符串/正则表达式搜索? –

+0

@Alexei Levenkov:嗯,我试图美化XML输出:在实际的代码中,我有很多'Inner'类,每个类都包含额外的名称空间(这看起来很丑陋)。无论如何,我想知道为什么XML序列化程序在两种情况下做了不同的事情,它们似乎是“相同的”。 – Vlad

+0

你有没有试过用'WriteAttributeString](http://msdn.microsoft.com/en-us/library/73z46xs1%28v=vs.110%29.aspx)在'OuterCustom'上手动编写xmlns属性? –

回答

1

首先 - 所提到的符合XML解析器的XML版本没有区别。只要节点具有关联的正确名称空间,定义名称空间前缀是否/如何/无关紧要。

由于这个问题是明确的风格在哪里把名称空间声明一个让自己的电话什么是好的,什么不是。在这个特定的情况下,目标是避免重复的xmlns属性并将它们移到树中更高的位置。

只要在同一节点上不冲突,就可以在需要的地方编写xmlns属性。使用XmlWriter.WriteAttributeString方法来添加它们:

writer.WriteAttributeString(
    "xmlns", "prefix", null, "urn:mynamespace"); 

为了避免重复申报 - 检查前缀已经使用XmlWriter.LookupPrefix

示例代码定命名空间定义,以保证xsdxsi前缀(弗拉德):

const string xsiNamespace = System.Xml.Schema.XmlSchema.InstanceNamespace; 
const string xsdNamespace = System.Xml.Schema.XmlSchema.Namespace; 

public static void EnsureDefaultNamespaces(System.Xml.XmlWriter writer) 
{ 
    if (writer.LookupPrefix(xsiNamespace) == null) 
     writer.WriteAttributeString("xmlns", "xsi", null, xsiNamespace); 
    if (writer.LookupPrefix(xsdNamespace) == null) 
     writer.WriteAttributeString("xmlns", "xsd", null, xsdNamespace); 
} 

如果需要进行更多更改以实现漂亮的XML,另一种方法是允许序列化完成并处理XML以调整前缀定义(即,规范化为所需的前缀,收集所有名称空间并定义所有的顶部)。可以用C#代码读取XML,甚至可以创建XSLT转换。