2012-09-25 27 views
31

¿Cómo elimino el espacio de nombres de la respuesta xml a continuación utilizando la API web?Eliminar el espacio de nombre en XML de la API web de 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

Usted podría intentar algo como esto: http: // stackoverflow.com/questions/29352015/how-can-i-create-custom-xml-namespace-attributes-when-consuming-a-legacy-soap-se –

Respuesta

37

La opción 1 es para cambiar al uso de XmlSerializer en GlobalConfiguration:

config.Formatters.XmlFormatter.UseXmlSerializer = true; 

opción 2 es para decorar sus modelos con

[DataContract(Namespace="")] 

(y si lo hace, usted necesita decorar los miembros con atributos [DataMember]).

+2

Hice UseXmlSerializer y ahora solo usa JSON. –

+4

'El atributo [DataContract()] requiere una referencia a la biblioteca [' System.Runtime.Serialization'] (http://msdn.microsoft.com/en-us/library/kd1dc9w5.aspx) – Andrew

+0

@MikeFlynn Corrí en el mismo problema. Si el XmlSerializer no puede serializar el objeto, intentará con Json en su lugar. No es realmente un IMO de comportamiento predeterminado esperado. Especialmente cuando NetDataContractSerializer arroja errores. – CDeutsch

20

Si está dispuesto a decorar su modelo con XmlRoot, esta es una buena manera de hacerlo. Supongamos que tienes un auto con puertas. La configuración WebAPI por defecto devolverá algo como:

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

Esto es lo que quiere:

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

Aquí es el modelo:

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

Lo que tienes que hacer es crear un XmlFormatter personalizada que tendrá un espacio de nombres vacío si no hay espacios de nombres definidos en el atributo XmlRoot. Por algún motivo, el formateador predeterminado siempre agrega los dos espacios de nombres predeterminados.

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

Lo último que se debe hacer es agregar el nuevo formateador en WebApiContext. Asegúrese de eliminar (o borrar) el antiguo XmlMediaTypeFormatter

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

Hola, me gusta su solución, sin embargo, no me da el sentido de llamar a la implementación base si ocurre una excepción. ¿Puedes explicar por qué has hecho eso? Gracias. –

+0

Desafortunadamente, ha pasado un tiempo desde que pirateé esto. Por lo que recuerdo, creo que fue una manera de omitir la eliminación de espacios de nombres XML y simplemente llamar al formateador "normal". – pobed2

+3

Gran trabajo. Pero, una palabra de advertencia: 'config.Formatters.Add (new IgnoreNamespacesXmlMediaTypeFormatter {UseXmlSerializer = true});' debe establecerse de otra manera mientras se envían datos 'POST',' FromBody' no podrá 'serializar'. –

5

Me gusta la respuesta de pobed2. Pero necesitaba el CustomNamespaceXmlFormatter para permitirme especificar un espacio de nombre de raíz predeterminado para usar cuando el atributo XmlRoot falta y también cuando está presente y no tiene ningún valor en la propiedad Namespace (es decir, el atributo se usa para establecer el nombre del elemento raíz solamente). Así que creé una versión mejorada, aquí está en caso de que sea útil para alguien:

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

Como se explica en [esta respuesta] (https://stackoverflow.com/a/23897411/3744182), para evitar una pérdida de memoria grave, un 'XmlSerializer' construido con un constructor no predeterminado se debe almacenar en caché y reutilizar estáticamente. Vea también [los documentos] (https://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializer.aspx#Remarks) which state * Si usa cualquiera de los otros constructores, varias versiones de el mismo ensamblado se genera y nunca se descarga, lo que da como resultado una pérdida de memoria y un rendimiento deficiente. ... De lo contrario, debe almacenar en caché los ensamblados en una tabla Hashtable, * – dbc

3

En el proyecto que mantiene a los modelos de respuesta van a Properties/AssemblyInfo.cs

Añadir

using System.Runtime.Serialization;

y al la parte inferior agrega

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

Reemplace Project.YourResponseModels con el espacio de nombre real donde se encuentran los modelos de respuesta. Es necesario agregar uno por espacio de nombres

+0

Hola, parece una solución temporal y se debe considerar como una mala práctica. Por ejemplo, en el caso de la refactorización de la estructura del proyecto, mis 'YourResponseModels' se pueden mover del espacio de nombres' Project.YourResponseModels' a 'Project.AnotherPlace.YourResponseModels'. Todos deberían tenerlo en cuenta en caso de cualquier refactorización. –

+0

Este era un enfoque antiguo para hacer que los cuerpos XML estuvieran limpios para el propósito de API web, por lo que no importaría cuando refactorices, el código y las entidades se estructuran como desees, solo el serializador manejaría cualquier cosa. En cuanto a la reestructuración, no creo que AssemblyInfo.cs contenga más de 10-20 líneas, aún es fácil de mantener. ¿Quién usaría XML en estos días de todos modos? Con WebAPI 2.0+, todo esto está resuelto, y JSON debería ser el único formato para las aplicaciones modernas. Si interactúa con sistemas antiguos, puede mantener espacios de nombres XML en su lugar de todos modos. –

-2

Esto funciona perfectamente

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