2012-01-11 54 views
4

我有一個「添加服務引用...」操作生成的對象,我用我寫的一個通用序列化器手動序列化它。刪除由串行器創建的空xmlns

我的問題是數據契約有一些內部對象。

序列化程序將一個空的名稱空間屬性添加到內部對象的起始標記。有什麼辦法可以阻止這種情況發生?

+5

爲什麼這是一個問題?如果元素處於默認名稱空間中,那麼'xmlns =「」'是正確的。 – 2012-01-11 16:27:58

+0

xmlns =「」可能是正確的,但我無法控制WS接收到它,並且它不會與它們一起工作。任何方式來刪除它們? – 2012-01-16 15:33:29

+0

如果xmlns =「」會破壞讀取XML的代碼,那麼它會被嚴重破壞,並且有一個非常糟糕的錯誤需要修復。它沒有閱讀一些基本的,正確的XML。確定是一個好鄰居,並告訴該代碼的所有者,他們應該修復它以處理默認名稱空間。 – 2012-01-17 15:27:32

回答

6

有關使您的內部對象是什麼屬於同一個命名空間的根?這樣,遺漏後代的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=""聲明。每Namespace Defaulting規則:

默認名稱空間聲明的範圍從它出現在相應的結束標記的端部開始標記的開始延伸,但不包括任何內默認命名空間聲明的範圍。在空標籤的情況下,範圍就是標籤本身。

因此,下面的代碼(有根元素指定namespace)...

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> 
+0

我最終使用了數據協定序列化程序而不是XmlSerializer,這使得它可以開箱即用,但非常感謝您的答覆。賞金賺了。 – 2012-01-19 02:25:24

+1

謝謝! :-)我打算提到'DataContractSerializer',但我錯誤地認爲服務本身爲其數據協定聲明瞭空的(或不正確的)名稱空間(例如'[DataContract(Namespace =「」)]'),在使用該序列化程序的客戶端並不容易。 'XmlSerializer'忽略數據合同中聲明的名稱空間,允許您替換自己的名稱空間。總之,很高興你找到了一個簡單的解決方案! – Douglas 2012-01-19 18:43:19

0

如果您可以控制序列化程序,則始終可以添加空名稱空間以確保從輸出XML中省略xmlns。例如:

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

最好的問候,

+0

我試過了,但是它從所有元素中移除了xmlns。我需要根仍然有xmlns(這不是空的)。 – 2012-01-16 16:05:03

0

這可能是有點偏離主題,並且可能不如果您使用[DataContract]則適用。相反,這適用於從WSDL生成的代理代碼(在帶有Java端點的互操作環境中,我們被告知xmlns =「」無效)。所以我把它放在那裏以防萬一。

當設置爲System.Xml.Schema.XmlSchemaForm.Unqualified時,XmlElementAttribute.Form屬性會導致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應該已經改變,但我最終編輯直接生成代理代碼(這絕對不是理想的),但實現了我的直接目標。