Entendí que los tipos anónimos están marcados como privados por el compilador y las propiedades son de solo lectura. ¿Hay alguna forma de serializarlos en xml (sin deserializar)? Es works with JSON, ¿cómo puedo hacerlo con XML?¿Puedo serializar tipos anónimos como xml?
Respuesta
No se puede lograr utilizando XmlSerializer
ni DataContractSerializer
. Se puede hacer mediante un código escrito manualmente, como se demostró en below (no puedo comentar si el código es lo suficientemente completo para manejar todos los tipos, pero es un muy buen comienzo).
Algo como esto debería empezar ...
class Program
{
static void Main(string[] args)
{
var me = new
{
Hello = "World",
Other = new
{
My = "Object",
V = 1,
B = (byte)2
}
};
var x = me.ToXml();
}
}
public static class Tools
{
private static readonly Type[] WriteTypes = new[] {
typeof(string), typeof(DateTime), typeof(Enum),
typeof(decimal), typeof(Guid),
};
public static bool IsSimpleType(this Type type)
{
return type.IsPrimitive || WriteTypes.Contains(type);
}
public static XElement ToXml(this object input)
{
return input.ToXml(null);
}
public static XElement ToXml(this object input, string element)
{
if (input == null)
return null;
if (string.IsNullOrEmpty(element))
element = "object";
element = XmlConvert.EncodeName(element);
var ret = new XElement(element);
if (input != null)
{
var type = input.GetType();
var props = type.GetProperties();
var elements = from prop in props
let name = XmlConvert.EncodeName(prop.Name)
let val = prop.GetValue(input, null)
let value = prop.PropertyType.IsSimpleType()
? new XElement(name, val)
: val.ToXml(name)
where value != null
select value;
ret.Add(elements);
}
return ret;
}
}
... XML resultante ...
<object>
<Hello>World</Hello>
<Other>
<My>Object</My>
<V>1</V>
<B>2</B>
</Other>
</object>
Supongo que 'Type.IsValueType' podría ser un buen atajo para la mayoría de los' IsAssignableFrom's. Sin embargo, no atrapa la 'cadena'. – sunside
'IsValueType' puede ser una elección incorrecta. Esto usaría el valor ToString para convertir el tipo. Pero hice un cambio que debería ser mucho más fácil de entender. –
Oye, tu código funciona como un hechizo. Hice algunos cambios menores, para que admita matrices. Escribí sobre esto aquí: http://martinnormark.com/serialize-c-dynamic-and-anonymous-types-to-xml – MartinHN
Gracias, excelente trabajo y @Matthew @ Martin.
He hecho algunas modificaciones para acomodar Nullables y Enums. También lo he cambiado para que los elementos de la matriz se nombren de acuerdo con el nombre de la propiedad + índice.
Este es el código si alguien está interesado
public static class ObjectExtensions {
#region Private Fields
private static readonly Type[] WriteTypes = new[] {
typeof(string), typeof(DateTime), typeof(Enum),
typeof(decimal), typeof(Guid),
};
#endregion Private Fields
#region .ToXml
/// <summary>
/// Converts an anonymous type to an XElement.
/// </summary>
/// <param name="input">The input.</param>
/// <returns>Returns the object as it's XML representation in an XElement.</returns>
public static XElement ToXml(this object input) {
return input.ToXml(null);
}
/// <summary>
/// Converts an anonymous type to an XElement.
/// </summary>
/// <param name="input">The input.</param>
/// <param name="element">The element name.</param>
/// <returns>Returns the object as it's XML representation in an XElement.</returns>
public static XElement ToXml(this object input, string element) {
return _ToXml(input, element);
}
private static XElement _ToXml(object input, string element, int? arrayIndex = null, string arrayName = null) {
if (input == null)
return null;
if (String.IsNullOrEmpty(element)) {
string name = input.GetType().Name;
element = name.Contains("AnonymousType")
? "Object"
: arrayIndex != null
? arrayName + "_" + arrayIndex
: name;
}
element = XmlConvert.EncodeName(element);
var ret = new XElement(element);
if (input != null) {
var type = input.GetType();
var props = type.GetProperties();
var elements = props.Select(p => {
var pType = Nullable.GetUnderlyingType(p.PropertyType) ?? p.PropertyType;
var name = XmlConvert.EncodeName(p.Name);
var val = pType.IsArray ? "array" : p.GetValue(input, null);
var value = pType.IsArray
? GetArrayElement(p, (Array)p.GetValue(input, null))
: pType.IsSimpleType() || pType.IsEnum
? new XElement(name, val)
: val.ToXml(name);
return value;
})
.Where(v=>v !=null);
ret.Add(elements);
}
return ret;
}
#region helpers
/// <summary>
/// Gets the array element.
/// </summary>
/// <param name="info">The property info.</param>
/// <param name="input">The input object.</param>
/// <returns>Returns an XElement with the array collection as child elements.</returns>
private static XElement GetArrayElement(PropertyInfo info, Array input) {
var name = XmlConvert.EncodeName(info.Name);
XElement rootElement = new XElement(name);
var arrayCount = input == null ? 0 : input.GetLength(0);
for (int i = 0; i < arrayCount; i++) {
var val = input.GetValue(i);
XElement childElement = val.GetType().IsSimpleType() ? new XElement(name + "_" + i, val) : _ToXml(val, null, i, name);
rootElement.Add(childElement);
}
return rootElement;
}
#region .IsSimpleType
public static bool IsSimpleType(this Type type) {
return type.IsPrimitive || WriteTypes.Contains(type);
}
#endregion .IsSimpleType
#endregion helpers
#endregion .ToXml
}
Este es un buen código (+1 ya), pero no maneja las Listas (La lista debe convertirse en Arrays, .ToArray(), eso no siempre es posible). – rufo
Mi propia versión de la continuación excelente trabajo y @Matthew @ Martin: Las matrices de las enumeraciones son ahora soportados y la noción de matrices en generalizada en IEnumerable con el fin de también es compatible con todo tipo de colecciones.
public static class ObjectExtensions {
/// <summary>
/// Converts an anonymous type to an XElement.
/// </summary>
/// <param name="input">The input.</param>
/// <returns>Returns the object as it's XML representation in an XElement.</returns>
public static XElement ToXml2(this object input) {
return input.ToXml2(null);
}
/// <summary>
/// Converts an anonymous type to an XElement.
/// </summary>
/// <param name="input">The input.</param>
/// <param name="element">The element name.</param>
/// <returns>Returns the object as it's XML representation in an XElement.</returns>
public static XElement ToXml2(this object input, string element) {
return _ToXml(input, element);
}
private static XElement _ToXml(object input, string element, int? arrayIndex = null, string arrayName = null) {
if (input == null)
return null;
if (String.IsNullOrEmpty(element)) {
string name = input.GetType().Name;
element = name.Contains("AnonymousType")
? "Object"
: arrayIndex != null
? arrayName + "_" + arrayIndex
: name;
}
element = XmlConvert.EncodeName(element);
var ret = new XElement(element);
if (input != null) {
var type = input.GetType();
var props = type.GetProperties();
var elements = props.Select(p => {
var pType = Nullable.GetUnderlyingType(p.PropertyType) ?? p.PropertyType;
var name = XmlConvert.EncodeName(p.Name);
var val = pType.IsArray ? "array" : p.GetValue(input, null);
var value = pType.IsEnumerable()
? GetEnumerableElements(p, (IEnumerable)p.GetValue(input, null))
: pType.IsSimpleType2() || pType.IsEnum
? new XElement(name, val)
: val.ToXml2(name);
return value;
})
.Where(v=>v !=null);
ret.Add(elements);
}
return ret;
}
#region helpers
private static XElement GetEnumerableElements(PropertyInfo info, IEnumerable input) {
var name = XmlConvert.EncodeName(info.Name);
XElement rootElement = new XElement(name);
int i = 0;
foreach(var v in input)
{
XElement childElement = v.GetType().IsSimpleType2() || v.GetType().IsEnum ? new XElement(name + "_" + i, v) : _ToXml(v, null, i, name);
rootElement.Add(childElement);
i++;
}
return rootElement;
}
private static readonly Type[] WriteTypes = new[] {
typeof(string), typeof(DateTime), typeof(Enum),
typeof(decimal), typeof(Guid),
};
public static bool IsSimpleType2(this Type type) {
return type.IsPrimitive || WriteTypes.Contains(type);
}
private static readonly Type[] FlatternTypes = new[] {
typeof(string)
};
public static bool IsEnumerable(this Type type) {
return typeof(IEnumerable).IsAssignableFrom(type) && !FlatternTypes.Contains(type);
}
#endregion
}
La respuesta por debajo de las manijas IEnumerables en la forma en que necesitaba y se convertirán esto:
new
{
Foo = new[]
{
new { Name = "One" },
new { Name = "Two" },
},
Bar = new[]
{
new { Name = "Three" },
new { Name = "Four" },
},
}
en esto:
<object>
<Foo><Name>One</Name></Foo>
<Foo><Name>Two</Name></Foo>
<Bar><Name>Three</Name></Bar>
<Bar><Name>Four</Name></Bar>
</object>
Así que aquí tienes, otra variante de la respuesta de Mateo :
public static class Tools
{
private static readonly Type[] WriteTypes = new[] {
typeof(string),
typeof(Enum),
typeof(DateTime), typeof(DateTime?),
typeof(DateTimeOffset), typeof(DateTimeOffset?),
typeof(int), typeof(int?),
typeof(decimal), typeof(decimal?),
typeof(Guid), typeof(Guid?),
};
public static bool IsSimpleType(this Type type)
{
return type.IsPrimitive || WriteTypes.Contains(type);
}
public static object ToXml(this object input)
{
return input.ToXml(null);
}
public static object ToXml(this object input, string element)
{
if (input == null)
return null;
if (string.IsNullOrEmpty(element))
element = "object";
element = XmlConvert.EncodeName(element);
var ret = new XElement(element);
if (input != null)
{
var type = input.GetType();
if (input is IEnumerable && !type.IsSimpleType())
{
var elements = (input as IEnumerable<object>)
.Select(m => m.ToXml(element))
.ToArray();
return elements;
}
else
{
var props = type.GetProperties();
var elements = from prop in props
let name = XmlConvert.EncodeName(prop.Name)
let val = prop.GetValue(input, null)
let value = prop.PropertyType.IsSimpleType()
? new XElement(name, val)
: val.ToXml(name)
where value != null
select value;
ret.Add(elements);
}
}
return ret;
}
}
- 1. ¿Cómo serializar objetos anónimos a XML?
- 2. tipos anónimos
- 3. JAXB: tipos simples anónimos como enumeraciones?
- 4. tipos anónimos y genéricos
- 5. ¿Colecciones de tipos anónimos?
- 6. WCF y tipos anónimos
- 7. IQueryable para tipos anónimos
- 8. ¿Puedo serializar tipos arbitrarios con protobuf-net?
- 9. XML Serializar objeto dinámico
- 10. jugar con los tipos anónimos
- 11. Declaración de tipos anónimos Lista
- 12. serializar clase a XML?
- 13. ¿Tipos anónimos a una matriz de objetos?
- 14. Cómo XML Serializar un 'Tipo'
- 15. Devolver tipos anónimos con API web
- 16. polimorfismo, genéricos y tipos anónimos C#
- 17. Tipos anónimos y Obtener acceso en WP7.1?
- 18. Tipos anónimos - ¿Hay alguna característica de distinción?
- 19. Optimizaciones del compilador de tipos anónimos
- 20. Rendimiento de tipos anónimos en C#
- 21. C# serializar decimal a xml
- 22. Cómo XML-serializar un diccionario
- 23. ¿Cómo puedo omitir la declaración xml al serializar?
- 24. ¿Serializar un objeto a JSON, XML, YAML?
- 25. serialización XML - XmlCDataSection como Serialization.XmlText
- 26. Serializar la fila de Oracle a XML
- 27. Json.net serializar/deserializar los tipos derivados?
- 28. distinto en LINQ con los tipos anónimos (en VB.NET)
- 29. C# solicitud de función: implementar interfaces de tipos anónimos
- 30. Serializar un System.Array a una Cadena XML
Eso no significa que no pueda sin embargo, hazlo con una clase de terceros. –
¿es posible escribir algo genérico? – Radu
@Radu: No sé a qué te refieres con "escribir algo genérico". No si estás hablando sobre usar el Serializador XML. La respuesta de "Matthew Whited" le muestra cómo hacerlo sin usar el Serializador XML. –