2012-06-18 28 views
19

Tengo una clase de datos que se serializa con el DataContractSerializer. La clase utiliza el atributo [DataContract] sin declaración explícita Namespace. Como tal, el espacio de nombres en el archivo xml resultante se genera en función del espacio de nombres de la clase.DataContractSerializer - cambiar el espacio de nombres y deserializar el archivo vinculado al antiguo espacio de nombres

La clase básicamente tiene el siguiente aspecto:

namespace XYZ 
{ 
    [DataContract] 
    public class Data 
    { 
     [DataMember(Order = 1)] 
     public string Prop1 { get; set; } 

     [DataMember(Order = 2)] 
     public int Prop2 { get; set; } 
    } 
} 

... y el XML resultante:

<?xml version="1.0" encoding="utf-8"?> 
<Data xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/XYZ"> 
    <Prop1>StringValue</Prop1> 
    <Prop2>11</Prop2> 
</Data> 

Ahora quieren cambiar el espacio de nombres de la clase (en realidad eliminarlo) cambiando el atributo [DataContract] a [DataContract(Namespace = "")]. Sin embargo, una vez que hago esto, cualquier archivo previamente serializado con el espacio de nombres original ya no se deserializa. Recibo la siguiente excepción:

Error in line 1 position XXX. Expecting element 'Data' from namespace ''.. Encountered 'Element' with name 'Data', namespace 'http://schemas.datacontract.org/2004/07/XYZ'.

Esto tiene mucho sentido. Cambié el espacio de nombres. Estoy bien con eso. Sin embargo, parece que debe haber una forma de decirle al DataContractSerializer que continúe y deserialice esos datos aunque los espacios de nombres no coincidan.

+0

¿Por qué quiere especificar una cadena vacía para un espacio de nombres en el atributo DataContract? ¿Qué estás ganando al hacer eso? –

+2

Gano sin estar atada al espacio de nombres CLR de la clase; y ese es realmente el problema aquí. Esta clase está cambiando su espacio de nombre CLR y no quiero estar vinculado a ese detalle de implementación. Podría, por supuesto, definir un valor de espacio de nombres xml que pueda mantener constante. Pero para esta implementación no veo el beneficio de hacer eso, simplemente eliminar el espacio de nombres xml. Esto se está utilizando para la serialización simple de archivos. – harlam357

Respuesta

15

Una posible forma es envolver el lector utilizado por el serializador en un lector que mapea el espacio de nombres anterior al nuevo, como se muestra a continuación. Mucho código, pero principalmente trivial.

public class StackOverflow_11092274 
{ 
    const string XML = @"<?xml version=""1.0"" encoding=""utf-8""?> 
<Data xmlns:i=""http://www.w3.org/2001/XMLSchema-instance"" xmlns=""http://schemas.datacontract.org/2004/07/XYZ""> 
    <Prop1>StringValue</Prop1> 
    <Prop2>11</Prop2> 
</Data>"; 

    [DataContract(Name = "Data", Namespace = "")] 
    public class Data 
    { 
     [DataMember(Order = 1)] 
     public string Prop1 { get; set; } 

     [DataMember(Order = 2)] 
     public int Prop2 { get; set; } 
    } 

    public class MyReader : XmlReader 
    { 
     XmlReader inner; 
     public MyReader(XmlReader inner) 
     { 
      this.inner = inner; 
     } 

     public override int AttributeCount 
     { 
      get { return inner.AttributeCount; } 
     } 

     public override string BaseURI 
     { 
      get { return inner.BaseURI; } 
     } 

     public override void Close() 
     { 
      inner.Close(); 
     } 

     public override int Depth 
     { 
      get { return inner.Depth; } 
     } 

     public override bool EOF 
     { 
      get { return inner.EOF; } 
     } 

     public override string GetAttribute(int i) 
     { 
      return inner.GetAttribute(i); 
     } 

     public override string GetAttribute(string name, string namespaceURI) 
     { 
      return inner.GetAttribute(name, namespaceURI); 
     } 

     public override string GetAttribute(string name) 
     { 
      return inner.GetAttribute(name); 
     } 

     public override bool IsEmptyElement 
     { 
      get { return inner.IsEmptyElement; } 
     } 

     public override string LocalName 
     { 
      get { return inner.LocalName; } 
     } 

     public override string LookupNamespace(string prefix) 
     { 
      return inner.LookupNamespace(prefix); 
     } 

     public override bool MoveToAttribute(string name, string ns) 
     { 
      return inner.MoveToAttribute(name, ns); 
     } 

     public override bool MoveToAttribute(string name) 
     { 
      return inner.MoveToAttribute(name); 
     } 

     public override bool MoveToElement() 
     { 
      return inner.MoveToElement(); 
     } 

     public override bool MoveToFirstAttribute() 
     { 
      return inner.MoveToFirstAttribute(); 
     } 

     public override bool MoveToNextAttribute() 
     { 
      return inner.MoveToNextAttribute(); 
     } 

     public override XmlNameTable NameTable 
     { 
      get { return inner.NameTable; } 
     } 

     public override string NamespaceURI 
     { 
      get 
      { 
       if (inner.NamespaceURI == "http://schemas.datacontract.org/2004/07/XYZ") 
       { 
        return ""; 
       } 
       else 
       { 
        return inner.NamespaceURI; 
       } 
      } 
     } 

     public override XmlNodeType NodeType 
     { 
      get { return inner.NodeType; } 
     } 

     public override string Prefix 
     { 
      get { return inner.Prefix; } 
     } 

     public override bool Read() 
     { 
      return inner.Read(); 
     } 

     public override bool ReadAttributeValue() 
     { 
      return inner.ReadAttributeValue(); 
     } 

     public override ReadState ReadState 
     { 
      get { return inner.ReadState; } 
     } 

     public override void ResolveEntity() 
     { 
      inner.ResolveEntity(); 
     } 

     public override string Value 
     { 
      get { return inner.Value; } 
     } 
    } 

    public static void Test() 
    { 
     DataContractSerializer dcs = new DataContractSerializer(typeof(Data)); 
     MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(XML)); 
     try 
     { 
      XmlReader r = XmlReader.Create(ms); 
      XmlReader my = new MyReader(r); 
      Data d = (Data)dcs.ReadObject(my); 
      Console.WriteLine("Data[Prop1={0},Prop2={1}]", d.Prop1, d.Prop2); 
     } 
     catch (Exception e) 
     { 
      Console.WriteLine(e); 
     } 
    } 
} 
+2

Esto funcionó muy bien! Estaba atacando el problema en el nivel equivocado. ¡Muchas gracias! Tengo algunos tipos de datos más complejos que contienen instancias 'Dictionary ' que resultaron un poco más difíciles. Las etiquetas para cada KVP han cambiado, supongo que en función del cambio del espacio de nombres, como tal: '' a ''. Tuve que realizar una conversión manual en estos archivos, pero está bien. Estos archivos/tipos representan una minoría de lo que debe leerse. – harlam357

Cuestiones relacionadas