删除序列化程序创建的空xmlns

时间:2012-01-11 16:26:54

标签: c# xml wcf

我有一个由“添加服务引用...”操作生成的对象,我手动使用我编写的通用序列化程序对其进行序列化。

我的问题是数据合约有一些内部对象。

序列化程序将空名称空间属性添加到内部对象的起始标记。有没有办法阻止这种情况发生?

3 个答案:

答案 0 :(得分:8)

如何使内部对象与根目录属于同一名称空间?这样,省略后代的xmlns声明是正确的。您可以使用[assembly: ContractNamespace]属性覆盖程序集中所有合同的命名空间。有关示例,请参阅Data Contract Names

编辑:详细说明如下。

假设您手动构建XML文档,并且没有为任何元素指定命名空间。

XDocument xmlDocument = new XDocument(
    new XElement("Book",
        new XElement("Title", "Animal Farm"),
        new XElement("Author", "George Orwell"),
        new XElement("Publisher",
            new XElement("Name", "Secker and Warburg"),
            new XElement("Location", "London"),
            new XElement("Founded", 1910))));
return xmlDocument.ToString();

正如预期的那样,生成的XML将没有名称空间声明:

<Book>
  <Title>Animal Farm</Title>
  <Author>George Orwell</Author>
  <Publisher>
    <Name>Secker and Warburg</Name>
    <Location>London</Location>
    <Founded>1910</Founded>
  </Publisher>
</Book>

但是,如果仅为根元素指定命名空间,则所有子元素必须使用xml=""声明显式地从该默认命名空间中恢复。根据{{​​3}}规则:

  

默认命名空间声明的范围从它出现的start-tag的开头延伸到相应的end-tag的末尾,不包括任何内部默认命名空间声明的范围。对于空标记,范围是标记本身。

因此,以下代码(具有为根元素指定的命名空间)...

XDocument xmlDocument = new XDocument(
    new XElement("{http://example.com/library}Book",
        new XElement("Title", "Animal Farm"),
        new XElement("Author", "George Orwell"),
        new XElement("Publisher",
            new XElement("Name", "Secker and Warburg"),
            new XElement("Location", "London"),
            new XElement("Founded", 1910))));
return xmlDocument.ToString();

...将提供以下XML:

<Book xmlns="http://example.com/library">
  <Title xmlns="">Animal Farm</Title>
  <Author xmlns="">George Orwell</Author>
  <Publisher xmlns="">
    <Name>Secker and Warburg</Name>
    <Location>London</Location>
    <Founded>1910</Founded>
  </Publisher>
</Book>

注意<Publisher>元素的子元素如何不需要从根的名称空间中恢复,因为它们从父元素继承了“no namespace”声明。

为了消除xmlns=""声明,为了演示,我们可以为所有后代分配相同的命名空间:

XDocument xmlDocument = new XDocument(
    new XElement("{http://example.com/library}Book",
        new XElement("{http://example.com/library}Title", "Animal Farm"),
        new XElement("{http://example.com/library}Author", "George Orwell"),
        new XElement("{http://example.com/library}Publisher",
            new XElement("{http://example.com/library}Name", "Secker and Warburg"),
            new XElement("{http://example.com/library}Location", "London"),
            new XElement("{http://example.com/library}Founded", 1910))));
return xmlDocument.ToString();

这将为XML文档提供仅在根中声明的命名空间(并在所有后代中隐式继承):

<Book xmlns="http://example.com/library">
  <Title>Animal Farm</Title>
  <Author>George Orwell</Author>
  <Publisher>
    <Name>Secker and Warburg</Name>
    <Location>London</Location>
    <Founded>1910</Founded>
  </Publisher>
</Book>

为了模仿涉及Web服务的场景,我们可以创建以下WCF服务。

[DataContract]
public class Book
{
    [DataMember]
    public string Title { get; set; }
    [DataMember]
    public string Author { get; set; }
    [DataMember]
    public Publisher Publisher { get; set; }
}

[DataContract]
public class Publisher
{
    [DataMember]
    public string Name { get; set; }
    [DataMember]
    public string Location { get; set; }
    [DataMember]
    public short Founded { get; set; }
}

[ServiceContract]
public interface ILibraryService
{
    [OperationContract]
    Book GetBook();
}

public class LibraryService : ILibraryService
{
    public Book GetBook()
    {
        return new Book
        {
            Title = "Animal Farm",
            Author = "George Orwell",
            Publisher = new Publisher
            {
                Name = "Secker and Warburg",
                Location = "London",
                Founded = 1910,
            }
        };
    }
}

我们在客户端应用程序中添加对上述服务的服务引用,使用它的操作,并将结果序列化,同时将其封装在具有显式命名空间的根Books元素中:

using (var libraryClient = new LibraryServiceReference.LibraryServiceClient())
{
    var book = libraryClient.GetBook();

    var stringBuilder = new StringBuilder();
    using (XmlWriter xmlWriter = XmlWriter.Create(stringBuilder))
    {
        xmlWriter.WriteStartElement("Books", "http://example.com/library");
        var serializer = new XmlSerializer(book.GetType());
        serializer.Serialize(xmlWriter, book);
        xmlWriter.WriteEndElement();
    }

    return stringBuilder.ToString();
}

在这种情况下,内部元素Book包含xmlns=""声明。

<?xml version="1.0" encoding="utf-16"?>
<Books xmlns="http://example.com/library">
  <Book xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
        xmlns="">
    <ExtensionData />
    <Author>George Orwell</Author>
    <Publisher>
      <ExtensionData />
      <Founded>1910</Founded>
      <Location>London</Location>
      <Name>Secker and Warburg</Name>
    </Publisher>
    <Title>Animal Farm</Title>
  </Book>
</Books>

如上所述,可以通过将xmlns=""元素的命名空间(及其后代的命名空间)设置为与根目录相对应来消除此Book。对于XmlSerializer类,可以通过其构造函数的第二个参数指定所有元素的默认命名空间。 (实际技术会根据您使用的序列化策略而有所不同。)

using (var libraryClient = new LibraryServiceReference.LibraryServiceClient())
{
    var book = libraryClient.GetBook();

    var stringBuilder = new StringBuilder();
    using (XmlWriter xmlWriter = XmlWriter.Create(stringBuilder))
    {
        xmlWriter.WriteStartElement("Books", "http://example.com/library");
        var serializer = new XmlSerializer(book.GetType(), "http://example.com/library");
        serializer.Serialize(xmlWriter, book);
        xmlWriter.WriteEndElement();
    }

    return stringBuilder.ToString();
}

这会产生预期的结果:

<?xml version="1.0" encoding="utf-16"?>
<Books xmlns="http://example.com/library">
  <Book xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <ExtensionData />
    <Author>George Orwell</Author>
    <Publisher>
      <ExtensionData />
      <Founded>1910</Founded>
      <Location>London</Location>
      <Name>Secker and Warburg</Name>
    </Publisher>
    <Title>Animal Farm</Title>
  </Book>
</Books>

答案 1 :(得分:1)

如果您可以控制序列化程序,则可以始终添加null命名空间以确保从输出XML中省略xmlns。例如:

var serializer = new XmlSerializer(target.GetType()); 
var ns = new XmlSerializerNamespaces(); 
ns.Add("",""); 
serializer.Serialize(xmlWriter, target, ns); 

致以最诚挚的问候,

答案 2 :(得分:1)

这可能有点偏离主题,如果您使用的是[DataContract],则可能不适用。相反,这适用于从WSDL生成的代理代码(在具有Java端点的互操作环境中,我们被告知xmlns =“”无效)。所以我把它放在那里以防它有用。

设置为XmlElementAttribute.Form时,System.Xml.Schema.XmlSchemaForm.Unqualified属性可以导致WCF请求中的子成员输出xmlns =“”。

[System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
public string MyProperty {
     get; set;
}

产生了

的内容
<MyObject xmlns="http://some.namespance">
   <MyProperty xmlns="">My value goes here</MyProperty>
</MyObject>

将其设置为System.Xml.Schema.XmlSchemaForm.None(这是默认值)意味着它不会输出“非限定”命名空间属性。

[System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.None)]
public string MyProperty {
    get; set;
}

产生这个:

<MyObject xmlns="http://some.namespance">
   <MyProperty>My value goes here</MyProperty>
</MyObject>

我不确定您是否可以在导入wsdl引用时更改此行为,或者可能wsdl应该已更改,但我最终直接编辑生成的代理代码(这绝对不是理想的),但已实现我的近期目标。