2012-09-25 64 views
31

如何使用Web API從下面的xml響應中刪除命名空間?從ASP.NET Web API中刪除XML中的命名空間

<ApiDivisionsResponse xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/GrassrootsHoops.Models.Api.Response"> 
<Divisions xmlns:d2p1="http://schemas.datacontract.org/2004/07/GrassrootsHoops.Data.Entities"> 
<d2p1:Page>1</d2p1:Page> 
<d2p1:PageSize>10</d2p1:PageSize> 
<d2p1:Results xmlns:d3p1="http://schemas.datacontract.org/2004/07/GrassrootsHoops.Models.Api.Response.Divisions"/> 
<d2p1:Total>0</d2p1:Total> 
</Divisions> 
</ApiDivisionsResponse> 
+0

你可以嘗試這樣的事: HTTP://計算器。com/questions/29352015/how-can-i-create-custom-xml-namespace-attributes-when-consume -a-legacy-soap-se –

回答

37

選項1是切換到使用XmlSerializerGlobalConfiguration

config.Formatters.XmlFormatter.UseXmlSerializer = true; 

方案2是

[DataContract(Namespace="")] 

(如果你這樣做,你來裝飾你的模型需要用[DataMember]屬性來修飾成員)。

+2

我做了UseXmlSerializer,現在它只是使用JSON。 –

+4

'[DataContract()]'屬性需要引用['System.Runtime.Serialization'](http://msdn.microsoft.com/en-us/library/kd1dc9w5.aspx)庫 – Andrew

+0

@MikeFlynn我跑了進入同一個問題。如果XmlSerializer無法序列化對象,它將嘗試使用Json。不是真正的預期默認行爲IMO。特別是當NetDataContractSerializer引發錯誤時。 – CDeutsch

20

如果你願意用XmlRoot來裝飾你的模型,這是一個很好的方法來實現它。假設你有一輛車門。默認的WebAPI配置將返回類似:

<car 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 
    <doors> 
     <door> 
      <color>black</color> 
     </door> 
    </doors> 
</car> 

這是你想要什麼:

<car> 
    <doors> 
     <door> 
      <color>black</color> 
     </door> 
    </doors> 
</car> 

這裏的模型:

[XmlRoot("car")] 
public class Car 
{ 
    [XmlArray("doors"), XmlArrayItem("door")] 
    public Door[] Doors { get; set; } 
} 

,你所要做的就是創建一個自定義XmlFormatter如果在XmlRoot屬性中沒有定義名稱空間,則它將具有空的名稱空間。出於某種原因,默認格式化程序總是添加兩個默認名稱空間。

public class CustomNamespaceXmlFormatter : XmlMediaTypeFormatter 
{ 
    public override Task WriteToStreamAsync(Type type, object value, Stream writeStream, HttpContent content, 
              TransportContext transportContext) 
    { 
     try 
     { 
      var xns = new XmlSerializerNamespaces(); 
      foreach (var attribute in type.GetCustomAttributes(true)) 
      { 
       var xmlRootAttribute = attribute as XmlRootAttribute; 
       if (xmlRootAttribute != null) 
       { 
        xns.Add(string.Empty, xmlRootAttribute.Namespace); 
       } 
      } 

      if (xns.Count == 0) 
      { 
       xns.Add(string.Empty, string.Empty); 
      } 

      var task = Task.Factory.StartNew(() => 
       { 
        var serializer = new XmlSerializer(type); 
        serializer.Serialize(writeStream, value, xns); 
       }); 

      return task; 
     } 
     catch (Exception) 
     { 
      return base.WriteToStreamAsync(type, value, writeStream, content, transportContext); 
     } 
    } 
} 

最後要做的事情是在WebApiContext中添加新的格式器。一定要刪除(或清除)舊的XmlMediaTypeFormatter

public static class WebApiContext 
{ 
    public static void Register(HttpConfiguration config) 
    { 
     ... 
     config.Formatters.Clear(); 
     config.Formatters.Add(new CustomNamespaceXmlFormatter{UseXmlSerializer=true}); 
     ... 
    } 
} 
+0

嗨,我喜歡你的解決方案,但是我不明白在發生異常時調用基本實現。你能解釋你爲什麼這樣做嗎?謝謝。 –

+0

不幸的是,這已經有一段時間了,因爲我已經對此進行了黑客攻擊。從我記憶中,我認爲它的目的是作爲一種簡單地跳過刪除XML名稱空間並簡單地調用「普通」格式化程序的方法。 – pobed2

+3

偉大的工作。但是,請注意:'config.Formatters.Add(新的IgnoreNamespacesXmlMediaTypeFormatter {UseXmlSerializer = true});'應該在發送'POST'數據時設置,否則'FromBody'將不能'序列化'它。 –

5

我喜歡pobed2的答案。但是我需要CustomNamespaceXmlFormatter來允許我指定默認的根名稱空間,以便在XmlRoot屬性缺失時使用,當它存在並且在Namespace屬性中沒有值時(即該屬性用於設置根元素名稱)。所以,我創建了一個改進版本,在這裏它是在情況下,它是非常有用的人:

public class CustomNamespaceXmlFormatter : XmlMediaTypeFormatter 
{ 
    private readonly string defaultRootNamespace; 

    public CustomNamespaceXmlFormatter() : this(string.Empty) 
    { 
    } 

    public CustomNamespaceXmlFormatter(string defaultRootNamespace) 
    { 
     this.defaultRootNamespace = defaultRootNamespace; 
    } 

    public override Task WriteToStreamAsync(
     Type type, 
     object value, 
     Stream writeStream, 
     HttpContent content, 
     TransportContext transportContext) 
    { 
     var xmlRootAttribute = type.GetCustomAttribute<XmlRootAttribute>(true); 
     if(xmlRootAttribute == null) 
      xmlRootAttribute = new XmlRootAttribute(type.Name) 
      { 
       Namespace = defaultRootNamespace 
      }; 
     else if(xmlRootAttribute.Namespace == null) 
      xmlRootAttribute = new XmlRootAttribute(xmlRootAttribute.ElementName) 
      { 
       Namespace = defaultRootNamespace 
      }; 

     var xns = new XmlSerializerNamespaces(); 
     xns.Add(string.Empty, xmlRootAttribute.Namespace); 

     return Task.Factory.StartNew(() => 
     { 
      var serializer = new XmlSerializer(type, xmlRootAttribute); 
      serializer.Serialize(writeStream, value, xns); 
     }); 
    } 
} 
+0

正如[本答案](https://stackoverflow.com/a/23897411/3744182)中所述,爲避免嚴重的內存泄漏,必須靜態緩存和重用使用非默認構造函數構造的「XmlSerializer」。另請參閱[文檔](https://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializer.aspx#Remarks)哪個狀態*如果您使用任何其他構造函數,則可以使用多個版本生成相同的程序集並且從不卸載,這會導致內存泄漏和性能下降。 ...否則,您必須將程序集緩存在Hashtable中,* – dbc

3

在這種不斷響應模型去Properties/AssemblyInfo.cs

添加

using System.Runtime.Serialization;

,並在項目底部加

[assembly: ContractNamespace("", ClrNamespace = "Project.YourResponseModels")] 

Project.YourResponseModels替換爲響應模型所在的實際名稱空間。 您需要添加每個命名空間有

+0

你好,看起來像解決方法,應該被認爲是一種不好的做法。例如,在項目結構重構的情況下,我的'YourResponseModels'可以從'Project.YourResponseModels'命名空間移動到'Project.AnotherPlace.YourResponseModels'。任何人在重構的時候都應該記住它。 –

+0

這是一種舊的方法,可以爲Web API目的清理XML主體,所以重構時無關緊要,代碼和實體的結構如你所願,只要序列化器可以處理任何事情。至於重組,我認爲AssemblyInfo.cs不會包含超過10-20行,仍然易於維護。無論如何,誰將會使用XML?有了WebAPI 2.0+,所有這些都解決了,JSON應該是現代aps的唯一格式。如果你使用舊系統進行交互,你可以保留XML名稱空間。 –

-2

一個這工作完全

public ActionResult JsonAction(string xxx) 
{ 
    XmlDocument xmlDoc2 = new XmlDocument(); 
    xmlDoc2.Load(xmlStreamReader); 

    XDocument d = XDocument.Parse(optdoc2.InnerXml); 
    d.Root.Attributes().Where(x => x.IsNamespaceDeclaration).Remove(); 

    foreach (var elem in d.Descendants()) 
    elem.Name = elem.Name.LocalName; 

    var xmlDocument = new XmlDocument(); 
    xmlDocument.Load(d.CreateReader()); 

    var jsonText = JsonConvert.SerializeXmlNode(xmlDocument); 
    return Content(jsonText); 
}