2012-09-25 19 views
31

Come rimuovere lo spazio dei nomi dalla risposta xml riportata di seguito utilizzando l'API Web?Rimuovere lo spazio dei nomi in XML dall'API Web ASP.NET

<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

Si potrebbe provare qualcosa di simile: http: // StackOverflow.it/questions/29352015/how-can-i-create-custom-xml-namespace-attributes-when-consuming-a-legacy-soap-se –

risposta

37

opzione 1 è quello di passare ad usare XmlSerializer in GlobalConfiguration:

config.Formatters.XmlFormatter.UseXmlSerializer = true; 

opzione 2 è quella di decorare i vostri modelli con

[DataContract(Namespace="")] 

(e se lo fate, saresti è necessario decorare i membri con gli attributi [DataMember]).

+2

Ho fatto UseXmlSerializer e ora usa solo JSON. –

+4

L'attributo '[DataContract()]' richiede un riferimento alla libreria ['System.Runtime.Serialization'] (http://msdn.microsoft.com/en-us/library/kd1dc9w5.aspx) – Andrew

+0

@MikeFlynn Ho eseguito nello stesso problema. Se XmlSerializer non può serializzare l'oggetto, proverà invece Json. Non proprio un comportamento predefinito previsto IMO. Soprattutto quando NetDataContractSerializer genera errori. – CDeutsch

20

Se sei disposto a decorare il tuo modello con XmlRoot, ecco un bel modo per farlo. Supponi di avere una macchina con le porte. La configurazione di default WebAPI tornerà qualcosa di simile:

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

Questo è ciò che si vuole:

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

Ecco il modello:

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

Quello che devi fare è creare un XmlFormatter personalizzato che avrà uno spazio dei nomi vuoto se non ci sono spazi dei nomi definiti nell'attributo XmlRoot. Per qualche ragione, il formattatore predefinito aggiunge sempre i due spazi dei nomi predefiniti.

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); 
     } 
    } 
} 

L'ultima cosa da fare è aggiungere il nuovo formattatore in WebApiContext. Assicurati di rimuovere (o cancellare) il vecchio XmlMediaTypeFormatter

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

Ciao, mi piace la tua soluzione, tuttavia non ho il coraggio di chiamare l'implementazione di base se si verifica un'eccezione. Puoi spiegare perché l'hai fatto? Grazie. –

+0

Sfortunatamente, è da un po 'che non ci riesco. Da quello che ricordo, penso che fosse inteso come un modo per saltare semplicemente la rimozione dei namespace XML e chiamare semplicemente il formattatore "normale". – pobed2

+3

Ottimo lavoro. Ma una parola di cautela: 'config.Formatters.Add (new IgnoreNamespacesXmlMediaTypeFormatter {UseXmlSerializer = true});' dovrebbe essere impostato diversamente durante l'invio dei dati 'POST',' FromBody' non sarà in grado di serializzarlo. –

5

Mi piace la risposta di pobed2. Ma avevo bisogno di CustomNamespaceXmlFormatter per permettermi di specificare uno spazio dei nomi radice predefinito da utilizzare quando l'attributo XmlRoot manca e anche quando è presente e non ha valore nella proprietà Namespace (vale a dire, l'attributo viene utilizzato per impostare la solo il nome dell'elemento radice). Così ho creato una versione migliorata, qui è nel caso in cui è utile per qualcuno:

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

Come spiegato in [questa risposta] (https://stackoverflow.com/a/23897411/3744182), per evitare una grave perdita di memoria, un 'XmlSerializer' costruito con un costruttore non predefinito deve essere staticamente memorizzato nella cache e riutilizzato. Vedi anche [i documenti] (https://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializer.aspx#Remarks) che dichiarano * Se usi uno qualsiasi degli altri costruttori, più versioni di lo stesso assembly viene generato e mai scaricato, il che si traduce in una perdita di memoria e scarse prestazioni. ... Altrimenti, è necessario memorizzare nella cache gli assembly in un Hashtable, * – dbc

3

Nel progetto che mantiene modelli di risposta vanno a Properties/AssemblyInfo.cs

Aggiungere

using System.Runtime.Serialization;

e al il fondo aggiungi

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

Sostituire Project.YourResponseModels con lo spazio dei nomi effettivo in cui si trovano i modelli di risposta. È necessario aggiungere uno per namespace

+0

Ciao, sembra una soluzione alternativa e deve essere considerato come una cattiva pratica. Ad esempio, in caso di refactoring della struttura del progetto il mio 'YourResponseModels' può essere spostato dallo spazio dei nomi' Project.YourResponseModels' a 'Project.AnotherPlace.YourResponseModels'. Tutti dovrebbero tenerlo a mente in caso di qualsiasi refactoring. –

+0

Questo era un vecchio approccio per rendere i corpi XML puliti per scopi API Web, quindi non importa quando si refactoring, il codice e le entità sono strutturati come si desidera, solo serializzatore gestirà qualsiasi cosa. Per quanto riguarda la ristrutturazione, non penso che AssemblyInfo.cs contenga più di 10-20 righe, ancora facili da mantenere. Chi userebbe XML oggigiorno comunque? Con WebAPI 2.0+ tutto questo è risolto e JSON dovrebbe essere l'unico formato per i moderni aps. Se si interfaccia con vecchi sistemi, è comunque possibile mantenere gli spazi dei nomi XML in posizione. –

-2

Questo funziona perfettamente

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); 
} 
Problemi correlati