2011-12-27 29 views
5

Ho una struttura di oggetti che sto tentando di serializzare in xml che risulta in un livello di nodo duplicato. Sono abbastanza sicuro che abbia qualcosa a che fare con la sottoclasse perché ho dovuto implementare la mia deserializzazione ma non sono sicuro di cosa stia succedendo nella direzione opposta. La stessa struttura xml viene utilizzata come input quando deserializza i miei dati all'avvio dell'applicazione come quando la riserializza per essere salvata in seguito.La serializzazione XML genera nodi duplicati

Ecco ciò che il xml uscita difettosa assomiglia:

<Keyboarding xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 
    <Exercises> 
    <Exercise> 
     <Exercise Id="3" ActivityNumber="5" SubActivityNumber="b" Type="Standard"> 
     <Title>Test Title</Title> 
     <Instructions>Downloaded Update Instructions</Instructions> 
     </Exercise> 
    </Exercise> 
    </Exercises> 
</Keyboarding> 

Qui è quello che dovrebbe essere simile (e si presenta come il deserializzazione iniziale):

<Keyboarding xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 
    <Exercises> 
     <Exercise Id="3" ActivityNumber="5" SubActivityNumber="b" Type="Standard"> 
     <Title>Test Title</Title> 
     <Instructions>Downloaded Update Instructions</Instructions> 
     </Exercise> 
    </Exercises> 
</Keyboarding> 

L'oggetto principale contiene una raccolta di esercizi . L'esercizio è la mia classe base, mentre le sottoclassi sono determinate dall'attributo Tipo. Sto usando un serializzatore xml personalizzato per costruire gli oggetti di vari tipi derivati ​​da Esercizio perché mi permette di usare il riflesso per abbinare qualsiasi delle dozzine circa di potenziali tipi derivati, che sono in ordine e quantità sconosciuti quando vengono letti per la prima volta dalla mia domanda.

Ecco la collezione del elemento root, utilizzando il serializzatore XML personalizzato:

[XmlArray("Exercises")] 
[XmlArrayItem("Exercise", Type = typeof (ExerciseXmlSerializer<Exercise>))] 
public Collection<Exercise> UnprocessedExercises { get; set; } 

La mia classe di esercizio di base è dichiarato come segue:

[Serializable] 
[XmlType(AnonymousType = true)] 
public class Exercise 

E la mia classe derivata è dichiarato come segue:

[Serializable] 
[XmlType(AnonymousType = true)] 
[XmlRoot(ElementName = "Exercise", IsNullable = false)] 
public class StandardExercise : Exercise 

Ecco la parte dello scrittore del mio serializzatore xml personalizzato:

public class ExerciseXmlSerializer<T> : IXmlSerializable where T : class 
{ 
    private T _data; 
    ... 
    public void WriteXml(XmlWriter writer) 
    { 
     Type type = _data.GetType(); 

     new XmlSerializer(type).Serialize(writer, _data); 
    } 
    ... 
} 

Nel metodo WriteXml(), la variabile type è impostata correttamente sul tipo derivato, quindi perché crea un livello di nodo per il tipo di base e il tipo derivato? Come posso impedirgli di farlo?

risposta

1

Credo che il problema è con questa linea:

[XmlArrayItem("Exercise", Type = typeof (ExerciseXmlSerializer<Exercise>))] 

Non credo che si vuole indicare che il tipo degli elementi sono un tipo di serializzatore. Se si desidera implementare IXmlSerializable, è necessario che sia nella classe da serializzare.

Ecco quello che ho avuto modo di lavorare:

public class Keyboarding 
{ 
    [XmlArray("Exercises")] 
    [XmlArrayItem("Exercise")] 
    public Collection<Exercise> UnprocessedExercises { get; set; } 

    public Keyboarding() 
    { 
     UnprocessedExercises = new Collection<Exercise>(); 
     UnprocessedExercises.Add(new StandardExercise()); 
    } 
} 

[XmlInclude(typeof(StandardExercise))] 
public class Exercise 
{ 
} 
[Serializable] 
public class StandardExercise : Exercise 
{ 
} 

Questo produce un output simile al seguente:

<?xml version=\"1.0\"?> 
<Keyboarding xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"> 
    <Exercises> 
    <Exercise xsi:type=\"StandardExercise\" /> 
    </Exercises> 
</Keyboarding> 

Se non si desidera che il xsi:type inclusi nell'output, è possibile utilizzare la risposta a this question.

+0

La formattazione si interrompe quando si tenta di deserializzare i dati iniziali, che ha lo stesso formato xml. Quando ho modificato il codice e l'xml iniziale per abbinarlo, ha digitato tutto come tipo di base durante la deserializzazione e ha perso tutte le sottoclassi. Stavo specificando il tipo di elementi come serializzatore xml perché ci sono quasi 2 dozzine di possibili classi derivate a cui è possibile digitare e un numero sconosciuto di elementi e ordini nell'xml. Questo mi consente di prendere l'attributo Type e utilizzare reflection nel mio serializzatore personalizzato per creare gli oggetti derivati. Modificherò la mia domanda per cercare di chiarire un po '. – HotN

+1

@HotN: hai provato a implementare il codice di serializzazione personalizzato direttamente nella classe Exercise? Ottengo eccezioni quando provo a eseguire il codice così com'è e questo è 'typeof (ExerciseXmlSerializer )' nell'XmlArrayItem che causa i problemi. Potresti anche provare a scrivere un xmlwriter personalizzato come mostrato nella risposta alla domanda collegata alla fine della mia risposta. –

+0

Il problema è stato sicuramente causato dal 'typeof (ExerciseXmlSerializer )' parte, ma quello che ho trovato è che la modifica che a poco Esercizio risolverebbe il problema di serializzazione, ma avrebbe rotto il mio deserializzazione in quanto i tipi di classe derivati ​​sono stati determinati da un attributo. L'ho fatto funzionare finalmente lasciando il codice originale in vigore per la collezione di esercizi ma implementando il mio XmlTextWriter, come suggerito. Sembra una specie di soluzione complicata, ma mi piace che non mi abbia richiesto di elencare 20 classi derivate come [XmlInclude]. Ad ogni modo, i risultati sono risultati. Grazie! – HotN

0

Dovresti essere in grado di utilizzare XmlAttributeOverrides per fare ciò che stai chiedendo. In questo caso, probabilmente faresti qualcosa del genere. Il codice di esempio è un po 'approssimativo perché non hai incluso l'intero grafico dell'oggetto, ma spero che questo ti induca nella giusta direzione.

List<Type> extraTypes = new List<Type>(); 
XmlAttributes attrs = new XmlAttributes(); 

extraTypes.Add(typeof(Exercise)); 
extraTypes.Add(typeof(StandardExercise)); 

XmlElementAttribute attr = new XmlElementAttribute 
{ 
    ElementName = "Exercise", 
    Type = typeof(Exercise) 
}; 
attrs.XmlElements.Add(attr); 

attr = new XmlElementAttribute 
{ 
    ElementName = "StandardExercise", 
    Type = typeof(StandardExercise) 
}; 
attrs.XmlElements.Add(attr); 

XmlAttributeOverrides overrides = new XmlAttributeOverrides(); 
overrides.Add(typeof(Keyboarding), "Keboarding", attrs); 

return new XmlSerializer(typeof(Keyboarding), overrides, extraTypes.ToArray(), 
    new XmlRootAttribute("Keyboarding"), string.Empty); 

Ciò dovrebbe consentire di creare un albero XML simile a questo. Non è esattamente quello che stavi cercando, ma è altrettanto descrittivo.

<Keyboarding xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 
    <Exercises> 
     <StandardExercise Id="3" ActivityNumber="5" SubActivityNumber="b"> 
      <Title>Test Title</Title> 
      <Instructions>Downloaded Update Instructions</Instructions> 
     </StandardExercise > 
    </Exercises> 
</Keyboarding> 
Problemi correlati