2012-06-27 19 views
7

Estoy creando un servicio WCF alojado en uno mismo. Estoy construyendo una estructura de datos especial para un transporte de datos muy flexible. Hasta ahora pruebo si mi estructura es serializable usando el DataContractSerializer. Eso funciona muy bien y estoy feliz por eso, pero hay algo que me molesta:Predefinir espacios de nombres XML para DataContractSerializer

En mi salida XML son redefinidas decenas xmlns atributos por ejemplo:

xmlns:a="http://schemas.microsoft.com/2003/10/Serialization/Arrays" 
xmlns:b="http://www.w3.org/2001/XMLSchema" 

Esto debería definirse mejor una vez en el elemento raíz para que los bytes puedan ser simplemente optimizados. ¿Hay alguna manera de agregar información personalizada del espacio de nombres al elemento raíz?

Aquí está un ejemplo grande para demostrar lo que quiero decir:

<DataObject xmlns="http://schemas.datacontract.org/2004/07/Test" 
      xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> 
    <Data xmlns:a="http://schemas.microsoft.com/2003/10/Serialization/Arrays"> 
    <a:KeyValueOfstringanyType> 
     <a:Key>ID</a:Key> 
     <a:Value i:type="b:int" xmlns:b="http://www.w3.org/2001/XMLSchema">1</a:Value> 
    </a:KeyValueOfstringanyType> 
    <a:KeyValueOfstringanyType> 
     <a:Key>Value</a:Key> 
     <a:Value i:type="b:int" xmlns:b="http://www.w3.org/2001/XMLSchema">42</a:Value> 
    </a:KeyValueOfstringanyType> 
    </Data> 
    <Data xmlns:a="...">...</Data> 
    <Data xmlns:a="...">...</Data> 
    <Data xmlns:a="...">...</Data> 
</DataObject> 

Lo que yo quiero es algo como esto:

<DataObject xmlns="http://schemas.datacontract.org/2004/07/Test" 
      xmlns:i="http://www.w3.org/2001/XMLSchema-instance" 
      xmlns:a="http://schemas.microsoft.com/2003/10/Serialization/Arrays" 
      xmlns:b="http://www.w3.org/2001/XMLSchema"> 
    <Data> 
    <a:KeyValueOfstringanyType> 
     <a:Key>ID</a:Key> 
     <a:Value i:type="b:int">1</a:Value> 
    </a:KeyValueOfstringanyType> 
    <a:KeyValueOfstringanyType> 
     <a:Key>Value</a:Key> 
     <a:Value i:type="b:int">42</a:Value> 
    </a:KeyValueOfstringanyType> 
    </Data> 
    <Data>...</Data> 
    <Data>...</Data> 
    <Data>...</Data> 
</DataObject> 
+0

(http [Cómo serializar un objeto a XML sin conseguir xmlns = “...”?]://stackoverflow.com/questions/258960/how-to-serialize-an-object-to-xml-without-getting-xmlns) <- Esto contiene lo que quiere –

+0

@MarkusJarderot no realmente. Esa parece ser una forma de deshacerse de los espacios de nombres en general. ¡Los contratos de WCF necesitan eso (hasta ahora lo sé)! Solo quiero evitar que haya redefiniciones serias. – rekire

Respuesta

12
static void Main() 
{ 
    var o = new Foo { 
     Prop = new Dictionary<string,string> { {"foo","bar"} } 
    }; 

    var ms = new MemoryStream(); 

    var slz = new DataContractSerializer(typeof(Foo)); 
    slz.WriteObject(ms, o, 
     new Dictionary<string,string> 
     { 
      { "arr", "http://schemas.microsoft.com/2003/10/Serialization/Arrays" }, 
     }); 

    string data = Encoding.UTF8.GetString(ms.ToArray()); 
    Console.WriteLine(data); 
} 

public static class Extensions 
{ 
    public static void WriteObject(
     this DataContractSerializer serializer, 
     Stream stream, object data, 
     Dictionary<string,string> namespaces) 
    { 
     using (var writer = XmlWriter.Create(stream)) 
     { 
      serializer.WriteStartObject(writer, data); 
      foreach (var pair in namespaces) 
      { 
       writer.WriteAttributeString("xmlns", pair.Key, null, pair.Value); 
      } 
      serializer.WriteObjectContent(writer, data); 
      serializer.WriteEndObject(writer); 
     } 
    } 
} 

[DataContract] 
class Foo 
{ 
    [DataMember] 
    public Dictionary<string,string> Prop; 
} 

Salida:

<?xml version="1.0" encoding="utf-8"?> 
<Foo xmlns:arr="http://schemas.microsoft.com/2003/10/Serialization/Arrays" 
    xmlns:i="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns="http://schemas.datacontract.org/2004/07/"> 
    <Prop> 
     <arr:KeyValueOfstringstring> 
      <arr:Key>foo</arr:Key> 
      <arr:Value>bar</arr:Value> 
     </arr:KeyValueOfstringstring> 
    </Prop> 
</Foo> 
+0

Eso se ve perfecto. Es una lástima que no haya un atributo que me agregue esa declaración. – rekire

+0

@rekire Es posible que desee consultar http://stackoverflow.com/a/6574024/22364 –

+0

Esa parece ser una solución adecuada. ¡Gracias de nuevo! Con 'DataContractSerializer' funciona todo bien. Pero el 'DataContractJsonSerializer' arroja una SerializationException hablando de un' DataContractResolver' o que debería usar 'KnownTypeAttribute'. ¿Por qué 'DataContractJsonSerializer' quiere eso pero' DataContractSerializer' no? – rekire

2

I utilizó con éxito la solución que se describe aquí : http://blogs.msdn.com/b/youssefm/archive/2009/07/24/optimizing-away-repeat-xml-namespace-declarations-with-datacontractserializer.aspx

Básicamente, crea un comportamiento que le agrega los espacios de nombres al elemento raíz.

Desde el artículo:

Basta con crear un serializador que hereda de XmlObjectSerializer que utiliza un DataContractSerializer para todos sus métodos, excepto por el hecho de que se registra espacios de nombres adicionales en el nivel superior. A continuación, cree un comportamiento que se deriva de DataContractSerializerOperationBehavior con un método CreateSerializer que devuelve el XmlObjectSerializer que acaba de crear y conecte el comportamiento.

En caso de que desee hacerlo en Silverlight, también puede utilizar la solución descrita aquí: http://blogs.msdn.com/b/carlosfigueira/archive/2011/05/24/wcf-extensibility-custom-serialization-in-silverlight.aspx

Cuestiones relacionadas