Los espacios de nombres y tema deserialización
-
01-10-2019 - |
Pregunta
ACTUALIZACIÓN: Se puede ejecutar el código al final de esta recrear y ver el error que estoy teniendo y es de esperar resolverlo
Update2: No es la eliminación de las xmlns = "" Esa es la cuestión ... como se puede quitarlo de la cadena XML inicial. El problema es con el [XmlType (TypeName = "sistemas")] de algún modo causando que se puede añadir ...
Update3: Resulta que el problema está en que aquí, tengo que establezca el nombre en base a lo que está en el vigente, XmlTypeAttribute si ya existe en la clase ....
xmlAttributes.XmlType = new XmlTypeAttribute
{
Namespace = ""
};
Me sale el siguiente XML como una cadena desde un servicio web
<Systems xmlns="">
<System id="1">
<sys_name>ALL</sys_name>
</System>
<System id="2">
<sys_name>asdfasdf</sys_name>
</System>
<System id="3">
<sys_name>fasdfasf</sys_name>
</System>
<System id="4">
<sys_name>asdfasdfasdf</sys_name>
</System>
</Systems>
A continuación, ejecuto esto, para convertirlo en un objeto
result = XElement.Parse(xmlResult.OuterXml).Deserialize<AwayRequestSystems>();
Extrañamente, sin embargo, en el método Deserialize, mientras que los RemoveAllNamespaces obras y devuelve el XML sin espacio de nombres
Me da la <Systems xmlns=''> was not expected.
error en la captura cuando ejecuta return (T) serializer.Deserialize(reader);
!
¿Por qué se hace esto? Los xmlns se ha ido !!!
código ejecutable! (Sólo hay que poner en un proyecto de prueba)
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Xml.Serialization;
namespace DeserializationTest
{
[TestClass]
public class UnitTest1
{
public TestContext TestContext { get; set; }
[TestMethod]
public void RemoveXmlnsFromSystems()
{
var xml = XElement.Parse(@"<Systems xmlns="""">
<System id=""1"">
<sys_name>ALL</sys_name>
</System>
<System id=""2"">
<sys_name>ePO</sys_name>
</System>
<System id=""3"">
<sys_name>iEFT</sys_name>
</System>
<System id=""4"">
<sys_name>Away Requests</sys_name>
</System>
<System id=""5"">
<sys_name>RP3</sys_name>
</System>
</Systems>");
var systems = xml.Deserialize<AwayRequestSystems>();
Assert.IsInstanceOfType(systems, typeof(AwayRequestSystems));
var xmlnsFree = xml.RemoveAllNamespaces();
var str = xmlnsFree.ToString();
Debug.WriteLine(str);
Assert.AreNotEqual("Error", xmlnsFree.Name.ToString(), "Serialization Error");
Assert.IsFalse(str.Contains("xmlns"), "Xmlns still exists");
}
}
[XmlType(TypeName = "Systems")]
public class AwayRequestSystems : List<AwayRequestSystem> { }
[XmlType(TypeName = "System")]
public class AwayRequestSystem
{
[XmlAttribute("id")]
public int ID { get; set; }
[XmlElement("sys_name")]
public string Name { get; set; }
}
public static class XmlSerializerFactory
{
private static Dictionary<Type, XmlSerializer> _serializers = new Dictionary<Type, XmlSerializer>();
public static void ResetCache()
{
_serializers = new Dictionary<Type, XmlSerializer>();
}
public static XmlSerializer GetSerializerFor(Type typeOfT)
{
if (!_serializers.ContainsKey(typeOfT))
{
var xmlAttributes = new XmlAttributes();
var xmlAttributeOverrides = new XmlAttributeOverrides();
Debug.WriteLine(string.Format("XmlSerializerFactory.GetSerializerFor(typeof({0}));", typeOfT));
xmlAttributes.XmlType = new XmlTypeAttribute
{
Namespace = ""
};
xmlAttributes.Xmlns = false;
var types = new List<Type> { typeOfT, typeOfT.BaseType };
foreach (var property in typeOfT.GetProperties())
{
types.Add(property.PropertyType);
}
types.RemoveAll(t => t.ToString().StartsWith("System."));
foreach (var type in types)
{
if (xmlAttributeOverrides[type] == null)
xmlAttributeOverrides.Add(type, xmlAttributes);
}
var newSerializer = new XmlSerializer(typeOfT, xmlAttributeOverrides);
//var newSerializer = new XmlSerializer(typeOfT, xmlAttributeOverrides, types.ToArray(), new XmlRootAttribute(), string.Empty);
//var newSerializer = new XmlSerializer(typeOfT, string.Empty);
_serializers.Add(typeOfT, newSerializer);
}
return _serializers[typeOfT];
}
}
public static class XElementExtensions
{
public static XElement RemoveAllNamespaces(this XElement source)
{
if (source.HasAttributes)
source.Attributes().Where(a => a.Name.LocalName.Equals("xmlns")).Remove();
return source.HasElements
? new XElement(source.Name.LocalName,
source.Attributes()/*.Where(a => !a.Name.LocalName.Equals("xmlns"))*/,
source.Elements().Select(el => RemoveAllNamespaces(el))
)
: new XElement(source.Name.LocalName)
{
Value = source.Value
};
}
}
public static class SerializationExtensions
{
public static XElement Serialize(this object source)
{
try
{
var serializer = XmlSerializerFactory.GetSerializerFor(source.GetType());
var xdoc = new XDocument();
using (var writer = xdoc.CreateWriter())
{
serializer.Serialize(writer, source, new XmlSerializerNamespaces(new[] { new XmlQualifiedName("", "") }));
}
var result = (xdoc.Document != null) ? xdoc.Document.Root : new XElement("Error", "Document Missing");
return result.RemoveAllNamespaces();
}
catch (Exception x)
{
return new XElement("Error", x.ToString());
}
}
public static T Deserialize<T>(this XElement source) where T : class
{
//try
//{
var serializer = XmlSerializerFactory.GetSerializerFor(typeof(T));
var cleanxml = source.RemoveAllNamespaces();
var reader = cleanxml.CreateReader();
return (T)serializer.Deserialize(reader);
//}
//catch (Exception x)
//{
// return null;
//}
}
}
}
Solución
El problema resulta que era porque estaba perdiendo el atributo TypeName al quitar espacios de nombres con esta línea
xmlAttributes.XmlType = new XmlTypeAttribute
{
Namespace = ""
};
Me cambió la clase GetSerializerFor en la fábrica hasta el siguiente y ahora funciona
public static XmlSerializer GetSerializerFor(Type typeOfT)
{
if (!_serializers.ContainsKey(typeOfT))
{
Debug.WriteLine(string.Format("XmlSerializerFactory.GetSerializerFor(typeof({0}));", typeOfT));
var types = new List<Type> { typeOfT, typeOfT.BaseType };
foreach (var property in typeOfT.GetProperties())
{
types.Add(property.PropertyType);
}
types.RemoveAll(t => t.ToString().StartsWith("System."));
var xmlAttributeOverrides = new XmlAttributeOverrides();
foreach (var type in types)
{
if (xmlAttributeOverrides[type] != null)
continue;
var xmlAttributes = new XmlAttributes
{
XmlType = new XmlTypeAttribute
{
Namespace = "",
TypeName = GetSerializationTypeName(type)
},
Xmlns = false
};
xmlAttributeOverrides.Add(type, xmlAttributes);
}
var newSerializer = new XmlSerializer(typeOfT, xmlAttributeOverrides);
//var newSerializer = new XmlSerializer(typeOfT, xmlAttributeOverrides, types.ToArray(), new XmlRootAttribute(), string.Empty);
//var newSerializer = new XmlSerializer(typeOfT, string.Empty);
_serializers.Add(typeOfT, newSerializer);
}
return _serializers[typeOfT];
}
private static string GetSerializationTypeName(Type type)
{
var xmlTypeAttribute = TypeDescriptor.GetAttributes(type)
.OfType<XmlTypeAttribute>().FirstOrDefault();
var typeName = xmlTypeAttribute.TypeName;
return string.IsNullOrEmpty(typeName) ? type.Name : typeName;
}