2011-12-27 15 views
5

Tengo una estructura de objeto que estoy tratando de serializar a xml que da como resultado un nivel de nodo duplicado. Estoy bastante seguro de que tiene algo que ver con la creación de subclases porque tuve que implementar mi propia deserialización, pero no estoy seguro de qué está pasando exactamente en la otra dirección. La misma estructura xml se usa como entrada al deserializar mis datos en el inicio de la aplicación, como cuando se reservó para guardarlos más tarde.Resultados de serialización XML en nodos duplicados

Esto es lo que se ve el código XML de salida defectuosa como:

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

Esto es lo que debe ser similar (y se ve como en la deserialización inicial):

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

El objeto raíz contiene una colección de ejercicios . El ejercicio es mi clase base, mientras que las subclases están determinadas por el atributo Tipo. Estoy usando un serializador xml personalizado para construir los objetos de diferentes tipos derivados de Exercise porque me permite usar el reflejo para que coincida con cualquiera de las 2 docenas de tipos derivados potenciales, que están en un orden y cantidad desconocidos cuando se leen por primera vez por mi aplicación.

Así es la colección del elemento raíz, utilizando el serializador XML personalizado:

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

Mi clase de ejercicios de base se declara como sigue:

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

Y mi clase derivada se declara como sigue:

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

Aquí está la parte del escritor de mi serializador xml personalizado:

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

En el método WriteXml(), la variable de tipo se establece correctamente en el tipo derivado, entonces ¿por qué crea un nivel de nodo tanto para el tipo base como para el derivado? ¿Cómo evito que lo haga?

Respuesta

1

Creo que su problema es con esta línea:

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

No creo que se desea indicar que el tipo de los artículos son un tipo de serializador. Si desea implementar IXmlSerializable, debe estar en la clase que se serializará.

Aquí es lo que tengo que trabajar:

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

Esto produce una salida similar a la siguiente:

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

Si no desea que el xsi:type incluido en la salida, se puede utilizar la respuesta al this question.

+0

Formateo como tal se rompe cuando intento deserializar los datos iniciales, que tienen el mismo formato xml. Cuando modifiqué el código y el xml inicial para que coincida con el tuyo, tipeó todo como el tipo base al deserializar y perdió todas las subclases. Estaba especificando el tipo de elementos como un serializador xml porque hay casi 2 docenas de posibles clases derivadas a las que se les puede escribir y un número desconocido de elementos y pedidos en el xml. Esto me permite tomar el atributo Tipo y usar la reflexión en mi serializador personalizado para construir los objetos derivados. Editaré mi pregunta para intentar aclarar un poco. – HotN

+1

@HotN: ¿Has intentado implementar tu código de serialización personalizado directamente en la clase de Ejercicio? Obtengo excepciones cuando trato de ejecutar el código como está y es este 'typeof (ExerciseXmlSerializer )' en el XmlArrayItem que está causando los problemas. También podría intentar escribir una xmlwriter personalizada como se muestra en la respuesta a la pregunta vinculada al final de mi respuesta. –

+0

El problema fue definitivamente causado por la parte 'typeof (ExerciseXmlSerializer )' pero lo que encontré es que cambiar eso solo a Exercise resolvería el problema de serialización pero rompería mi deserialización ya que los tipos de clase derivados estaban determinados por un atributo. Finalmente funcioné dejando el código original en su lugar para la colección de ejercicios pero implementando mi propio XmlTextWriter, como sugirió. Se siente como una especie de solución intrincada, pero me gusta que no me requirió enumerar 20 clases derivadas como [XmlInclude]. De todos modos, los resultados son resultados. ¡Gracias! – HotN

0

Debería poder usar XmlAttributeOverrides para hacer lo que está pidiendo. En este caso, probablemente harías algo como esto. El código de muestra es un poco difícil porque no incluyó todo su gráfico de objetos, pero con suerte esto lo orientará en la dirección correcta.

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

Esto debería permitirle crear un árbol XML que se vea así. No es exactamente lo que estabas buscando, pero es igual de descriptivo.

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