我可以将匿名类型序列化为 xml 吗?

问题描述 投票:0回答:10

我知道匿名类型被编译器标记为私有,并且属性是只读的。有没有办法将它们序列化为 xml(无需反序列化)?它适用于 JSON,我如何使用 XML 来做到这一点?

.net xml serialization anonymous-types
10个回答
72
投票

这样的事情应该让你开始......

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

<object>
  <Hello>World</Hello>
  <Other>
    <My>Object</My>
    <V>1</V>
    <B>2</B>
  </Other>
</object>

28
投票

使用

XmlSerializer
DataContractSerializer
都无法完成。它可以通过手动编写的代码来完成,如下所示(我无法评论代码是否足够全面以处理所有类型 - 但这是一个非常好的开始)。


27
投票
谢谢@Matthew 和@Martin,干得很好。

我做了一些修改来适应 Nullables 和 Enums。我还更改了它,以便根据属性名称+索引来命名数组元素。

如果有人感兴趣的话,这是代码

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 }
    

16
投票
我知道这是一篇旧文章,但我的解决方案仅用

2 行代码即可将匿名类型转换为 XML。

首先将匿名类型转换为 JSON,然后从 JSON 转换为 XML。

var jsonText = JsonConvert.SerializeObject(data); // convert to JSON XmlDocument doc = JsonConvert.DeserializeXmlNode(jsonText); // convert JSON to XML Document

样品

var data = new // data - Anonymous Type { Request = new { OrderNumber = 123, Note = "Hello World" } }; var jsonText = JsonConvert.SerializeObject(data); XmlDocument doc = JsonConvert.DeserializeXmlNode(jsonText); Console.WriteLine(doc.OuterXml);

输出

<Request> <OrderNumber>123</OrderNumber> <Note>Hello World</Note> </Request>
    

4
投票
我自己版本的当时出色的工作@Matthew和@Martin:现在支持枚举数组,并且数组的概念被推广到IEnumerable中,以便也支持所有类型的集合。

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 }
    

3
投票
下面的答案以我需要的方式处理 IEnumerables 并将其转变:

new { Foo = new[] { new { Name = "One" }, new { Name = "Two" }, }, Bar = new[] { new { Name = "Three" }, new { Name = "Four" }, }, }

进入这个:

<object> <Foo><Name>One</Name></Foo> <Foo><Name>Two</Name></Foo> <Bar><Name>Three</Name></Bar> <Bar><Name>Four</Name></Bar> </object>

所以,这是马修答案的另一个变体:

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

0
投票
另一张海报 Pavel 提到使用 Newtonsoft.Json 方法进行快速 2 行转换

var jsonText = JsonConvert.SerializeObject(data); XmlDocument doc = JsonConvert.DeserializeXmlNode(jsonText);
我建议这实际上可以是一行代码:)

using Newtonsoft.Json; using Newtonsoft.Json.Linq; XmlDocument doc = JsonConvert.DeserializeXmlNode(JsonConvert.SerializeObject(data));
    

0
投票
我对 Jeremy Cook 的答案做了一个小改动,以允许使用属性、命名空间以及它如何处理列表。

将转动这个:

var sample = new { versionAttribute = "v10.2", Foo = new { IdAttribute = "ID_50", Name = "Jhon", NickName = "Jonny", }, Bar = new[] { new { Name = "Three" }, new { Name = "Four" }, }, };
进入这个:

<Customer version="v10.2" xmlns="http://www.test.org.br/customer.xsd"> <Foo Id="ID_50"> <Name>Jhon</Name> <NickName>Jonny</NickName> </Foo> <Bar> <Name>Three</Name> <Name>Four</Name> </Bar> </Customer>
这是完整的代码:

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 XElement ToXml(this object input) { return input.ToXml(null); } public static XElement ToXml(this object input, XElement root) { if (input == null) return null; if (root == null) root = new XElement("Root"); var ret = new XElement(root); if (input != null) { var type = input.GetType(); if (input is IEnumerable && !type.IsSimpleType()) { var elements = (input as IEnumerable<object>) .Select(m => m.ToXml(root).FirstNode) .ToArray(); ret.Add(elements); } else { var props = type.GetProperties(); var elements = props.Select(prop => { var name = XmlConvert.EncodeName(prop.Name); var val = prop.GetValue(input, null); XElement value = null; if (prop.PropertyType.IsSimpleType()) { if (name.EndsWith("Attribute")) { ret.SetAttributeValue(name.Replace("Attribute", ""), val); } else { value = new XElement(root.Name.Namespace + name, val); } } else { value = val.ToXml(new XElement(root.Name.Namespace + name)); } return value; }).Where(value => value != null); ret.Add(elements); } } return ret; } }`
使用示例:

XNamespace nsA = "http://www.test.org.br/customer.xsd"; XElement rootElement = new XElement(nsA + "Customer"); //you can add other namespaces and attributes using XElement sample.ToXml(rootElement)
    

-1
投票
我第一篇为网站做出贡献的帖子,总是有很大帮助 Bellow是一个完整的解决方案,可以与.net core一起使用(使用3.1测试) 只需调用 ConvertAnonymousToType.Convert("MyNewType", target, out Type newType) 返回将是一个对象,可用于在控制器内返回或手动序列化 (System.Xml.Serialization.XmlSerializer)。 使用该对象作为控制器内部的返回: 1.配置服务(Startup.cs > ConfigureServices > services.AddMvcCore(options => options.OutputFormatters.Add(new XmlSerializerOutputFormatter());),

2. 目标操作:使用[FormatFilter]和[Route(".......{format}")], 3.归还对象

如果需要任何额外的匿名类型来组成回复(元数据、结果集等),DynamicTypeBuilder.CreateNewObject 可用于执行服务:)

再次感谢

using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Reflection.Emit; namespace Configuration { public static class DynamicTypeBuilder { public static object CreateNewObject(string typeName, Dictionary<string, Type> properties, out Type newType) { newType = CompileResultType(typeName, properties); return Activator.CreateInstance(newType); } public static Type CompileResultType(string typeName, Dictionary<string, Type> properties) { TypeBuilder tb = GetTypeBuilder(typeName); ConstructorBuilder constructor = tb.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName); //Add properties properties.ToList().ForEach(p => CreateProperty(tb, p.Key, p.Value)); //Created Type with properties Type objectType = tb.CreateType(); return objectType; } //Create Type with an standard configuration private static TypeBuilder GetTypeBuilder(string typeName) { string assemblyName = typeName + "InternalAssembly"; var an = new AssemblyName(assemblyName); AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run); ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule"); TypeBuilder tb = moduleBuilder.DefineType(typeName, TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.AutoClass | TypeAttributes.AnsiClass | TypeAttributes.BeforeFieldInit | TypeAttributes.AutoLayout, null); return tb; } private static void CreateProperty(TypeBuilder tb, string propertyName, Type propertyType) { FieldBuilder fieldBuilder = tb.DefineField("_" + propertyName, propertyType, FieldAttributes.Private); PropertyBuilder propertyBuilder = tb.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, null); MethodBuilder getPropMthdBldr = tb.DefineMethod("get_" + propertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, propertyType, Type.EmptyTypes); ILGenerator getIl = getPropMthdBldr.GetILGenerator(); getIl.Emit(OpCodes.Ldarg_0); getIl.Emit(OpCodes.Ldfld, fieldBuilder); getIl.Emit(OpCodes.Ret); MethodBuilder setPropMthdBldr = tb.DefineMethod("set_" + propertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, null, new[] { propertyType }); ILGenerator setIl = setPropMthdBldr.GetILGenerator(); Label modifyProperty = setIl.DefineLabel(); Label exitSet = setIl.DefineLabel(); setIl.MarkLabel(modifyProperty); setIl.Emit(OpCodes.Ldarg_0); setIl.Emit(OpCodes.Ldarg_1); setIl.Emit(OpCodes.Stfld, fieldBuilder); setIl.Emit(OpCodes.Nop); setIl.MarkLabel(exitSet); setIl.Emit(OpCodes.Ret); propertyBuilder.SetGetMethod(getPropMthdBldr); propertyBuilder.SetSetMethod(setPropMthdBldr); } } public static class ConvertAnonymousToType { public static object Convert(string typeName, object target, out Type newType) { var properties = GetProperties(target); newType = DynamicTypeBuilder.CompileResultType(typeName, properties); return Convert(newType, target); } public static object Convert(Type type, object target) { if (target.GetType().Name == typeof(List<>).Name) { var newListType = typeof(List<>).MakeGenericType(type); var newList = Activator.CreateInstance(newListType); MethodInfo addMethod = newList.GetType().GetMethod("Add"); ((IList<object>)target).ToList().ForEach(e => { addMethod.Invoke(newList, new object[] { ConvertObject(type, e) }); }); return newList; } else { return ConvertObject(type, target); } } private static object ConvertObject(Type type, object refObject) { Dictionary<string, Type> properties = new Dictionary<string, Type>(); object newObject = Activator.CreateInstance(type); var propertiesOrg = refObject.GetType().GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).ToList(); var propertiesDes = newObject.GetType().GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).ToList(); propertiesOrg.ForEach(po => propertiesDes.First(pd => pd.Name == po.Name).SetValue(newObject, po.GetValue(refObject))); return newObject; } private static Dictionary<string, Type> GetProperties(object target) { object objectRef = target; if (target.GetType().Name == typeof(List<>).Name) objectRef = ((List<object>)target).ToList()[0]; Dictionary<string, Type> properties = new Dictionary<string, Type>(); var lstProperties = objectRef.GetType().GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).ToList(); lstProperties.ForEach(p => properties.Add(p.Name, p.PropertyType)); return properties; } } }
    

-1
投票
我发现这个页面非常有用。我正在从旧的 SOAP Web 引用转向 .net 标准中支持的内容。我已经更改了代码,以便能够以可与 SOAP Web 服务配合使用的格式对其进行序列化。它很粗糙,但它可以将

object[]

 转换为 
<anyType />
 元素,其他简单数组可以正确序列化,您可以传入命名空间或让它在模型上使用 
XmlTypeAttribute
 来分配命名空间。

public static class AnonymousTypeSerializer { private static readonly XNamespace _xmlInstanceNs = "http://www.w3.org/2001/XMLSchema-instance"; private static readonly Type[] WriteTypes = new[] { typeof(string), typeof(DateTime), typeof(Enum), typeof(decimal), typeof(Guid), }; private static readonly Dictionary<Type, string> SerializedTypeNames = new Dictionary<Type, string>() { { typeof(int), "int" }, { typeof(long), "long" } }; /// <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) => 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, XNamespace? ns = null) => _ToXml(input, element, ns ?? XNamespace.None); private static XElement? _ToXml(object? input, string? element, XNamespace? ns = null, 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}" : name; } element = XmlConvert.EncodeName(element); var ret = new XElement(ns.GetName(element)); if (input != null) { var type = input.GetType(); var props = type.GetProperties(); var xmlType = type.GetCustomAttribute<XmlTypeAttribute>(true); if (xmlType != null && xmlType.Namespace != null) ns = xmlType.Namespace; ret.Add(props.Select(p => { var pType = Nullable.GetUnderlyingType(p.PropertyType) ?? p.PropertyType; string name = XmlConvert.EncodeName(p.Name); object val = pType.IsArray ? "array" : p.GetValue(input, null); var value = pType.IsArray ? GetArrayElement(p, (Array)p.GetValue(input, null), ns) : pType.IsSimpleType() || pType.IsEnum ? new XElement(ns.GetName(name), val) : val.ToXml(name, ns); return value; }).Where(v => v != null)); } return ret; } /// <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, XNamespace ns) { string name = XmlConvert.EncodeName(info.Name); if (input == null) return null; int arrayCount = input.GetLength(0); var elementType = input.GetType().GetElementType(); bool isAnyType = elementType == typeof(object); XElement rootElement; if (isAnyType) rootElement = new XElement(ns + name, new XAttribute(XNamespace.Xmlns + "xsi", _xmlInstanceNs)); else rootElement = new XElement(ns + name); for (int i = 0; i < arrayCount; i++) { object val = input.GetValue(i); if (isAnyType) { var valType = val.GetType(); var xmlType = valType.GetCustomAttribute<XmlTypeAttribute>(true); var valNs = ns; if (xmlType != null && xmlType.Namespace != null) valNs = xmlType.Namespace; // Create anyType element var childElement = new XElement(ns + "anyType", new XAttribute(XNamespace.Xmlns + $"p{rootElement.Elements().Count()}", valNs)); // Create type attribute string nsPrefix = childElement.GetPrefixOfNamespace(valNs); childElement.Add(new XAttribute(_xmlInstanceNs + "type", !string.IsNullOrEmpty(nsPrefix) ? $"{nsPrefix}:{valType.Name}" : valType.Name)); // Create child elements var inner = _ToXml(val, null, ns, i, "anyType"); childElement.Add(inner!.Elements()); // Done rootElement.Add(childElement); } else { if (!SerializedTypeNames.TryGetValue(elementType, out string typeName)) typeName = elementType.Name; rootElement.Add(elementType.IsSimpleType() ? new XElement(ns.GetName(typeName.ToLower()), val) : _ToXml(val, null, ns, i, typeName)); } } return rootElement; } public static bool IsSimpleType(this Type type) => type.IsPrimitive || WriteTypes.Contains(type); }
    
© www.soinside.com 2019 - 2024. All rights reserved.