2017-07-10 2 views
1

Je désérialisation XML à des objets avec cette méthode:Erreur désérialisation XML avec DataContractSerializer

public T Deserialize(string filename) 
{ 
    var xml = File.ReadAllText(filename); 
    MemoryStream stream = new MemoryStream(); 
    StreamWriter writer = new StreamWriter(stream); 
    writer.Write(xml); 
    writer.Flush(); 
    stream.Position = 0; 
    DataContractSerializer dcs = new DataContractSerializer(typeof(T)); 
    T obj = (T)dcs.ReadObject(stream); 
    return obj; 
} 

J'ai un vieux XML, et depuis quelques propriétés ajoutées/retirés de la classe je sérialisation/désérialisation.

J'ai l'exception suivante:

objet désérialisé avec identifiant de référence 'I53' introuvable dans le flux.

Est-il possible de personnaliser le DataContractSerializer pour ignorer une propriété si elle n'est plus dans le modèle? Notez que la propriété deleted est une référence à un autre objet complexe, pas au type simple. Le fichier XML contient cela, ma classe n'est plus.

+0

Copie possible de [Comment puis-je ignorer une propriété lors de la sérialisation à l'aide de DataContractSerializer?] (Https://stackoverflow.com/questions/1791946/how-can-i-ignore-a-property-when-serializing-using -the-datacontrerserializer) –

+0

@WimOmbelets Veuillez lire les deux questions. Ils sont deux sujets différents ... – jannagy02

+0

Nous avons besoin de voir un [mcve] de votre problème. Il semble que quelque chose ne va pas avec [suivi de référence d'objet] (https://stackoverflow.com/questions/1037201/isreference-property-in-data-contract). Par exemple, vous avez peut-être obsolète un membre de données où un objet est défini, ainsi une référence ultérieure à l'objet dans le XML via un attribut '" z: Ref "' échoue. – dbc

Répondre

1

Le message d'exception Deserialized object with reference id 'i53' not found in stream peut s'afficher lorsque vous activez la fonctionnalité object reference preservation du sérialiseur de contrat de données. Il indique que, lors de la désérialisation, une référence à un objet indéfini a été rencontrée, et ne peut donc pas être désérialisée.

J'ai été capable de reproduire le problème en obsolérant un membre de données comme suit. Tout d'abord, je définissais les types suivants:

namespace V1 
{ 
    [DataContract(Name = "Member", Namespace = "Question45008433", IsReference = true)] 
    public class Member 
    { 
     [DataMember] 
     public string Name { get; set; } 
    } 

    [DataContract(Name = "Root", Namespace = "Question45008433")] 
    public class RootObject 
    { 
     [DataMember(Order = 1)] 
     public Member MainMember { get; set; } 

     [DataMember(Order = 2)] 
     public List<Member> Members { get; set; } 
    } 
} 

Puis j'ai créé un objet de test comme suit:

var list = new List<V1.Member> { new V1.Member { Name = "Foo" }, new V1.Member { Name = "Bar" } }; 
var v1 = new V1.RootObject { MainMember = list[0], Members = list }; 

Notez que la fois de MainMember objet Foo est appelé deux fois, et une fois dans la liste Members .

Quand je sérialisé cela en utilisant DataContractSerializer, j'ai le code XML suivant:

<Root xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="Question45008433"> 
    <MainMember z:Id="i1" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/"> 
    <Name>Foo</Name> 
    </MainMember> 
    <Members> 
    <Member z:Ref="i1" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" /> 
    <Member z:Id="i2" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/"> 
     <Name>Bar</Name> 
    </Member> 
    </Members> 
</Root> 

Notez que l'objet Foo est entièrement sérialisé quand il est premier sérialisé <MainMember>, après quoi il est donné un attribut z:Id="i1". Lorsque des références ultérieures sont rencontrées pendant la sérialisation, seule une référence est sérialisée via z:Ref="i1".

Ensuite, j'ai décidé que le membre de données MainMember était inutile et obsolète il:

namespace V2 
{ 
    [DataContract(Name = "Member", Namespace = "Question45008433", IsReference = true)] 
    public class Member 
    { 
     [DataMember] 
     public string Name { get; set; } 
    } 

    [DataContract(Name = "Root", Namespace = "Question45008433")] 
    public class RootObject 
    { 
     [DataMember(Order = 2)] 
     public List<Member> Members { get; set; } 
    } 
} 

Maintenant, si j'essaie de désérialiser le XML original en utilisant ce contrat modifié, je reçois l'exception même que vous voyez:

System.Runtime.Serialization.SerializationException: Deserialized object with reference id 'i1' not found in stream. 
    at System.Runtime.Serialization.XmlObjectSerializerReadContext.GetExistingObject(String id, Type type, String name, String ns) 
    at System.Runtime.Serialization.XmlObjectSerializerReadContext.TryHandleNullOrRef(XmlReaderDelegator reader, Type declaredType, String name, String ns, Object& retObj) 
    at System.Runtime.Serialization.XmlObjectSerializerReadContext.InternalDeserialize(XmlReaderDelegator reader, String name, String ns, DataContract& dataContract) 

Pourquoi cela se produit-il? Cela se produit parce que le membre de données obsolète est venu avant les membres de données restants. Ainsi, lors de la désérialisation, l'élément de définition est ignoré et ignoré, et les références suivantes ne peuvent pas être résolues.

La solution consiste à rajouter le membre de données d'origine en tant que propriété synthétique faux privée qui ne fait rien et retourne toujours null:

namespace V3 
{ 
    [DataContract(Name = "Member", Namespace = "Question45008433", IsReference = true)] 
    public class Member 
    { 
     [DataMember] 
     public string Name { get; set; } 
    } 

    [DataContract(Name = "Root", Namespace = "Question45008433")] 
    public class RootObject 
    { 
     [DataMember(EmitDefaultValue = false, Order = 1)] 
     Member MainMember 
     { 
      get 
      { 
       return null; 
      } 
      set 
      { 
       // Do nothing 
      } 
     } 


     [DataMember(Order = 2)] 
     public List<Member> Members { get; set; } 
    } 
} 

Le XML d'origine peut maintenant être désérialisée avec succès parce que, lors de la désérialisation, les données Le sérialiseur de contrat lui-même maintient une table de consultation de tous les éléments de référence par leur nom. Toutefois, l'élément z:Ref="i1" est ajouté uniquement lorsqu'il est rencontré s'il correspond à un membre actuellement valide. Et, parce que EmitDefaultValue = false, l'élément obsolète n'apparaîtra plus lors de la sérialisation.

+1

Merci pour ce super effort, vous avez parfaitement reproduit et résolu mon problème que je n'ai même pas bien décrit. – jannagy02