.net 我可以将匿名类型序列化为 xml 吗?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2404685/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Can I serialize Anonymous Types as xml?
提问by Radu
I understood that anonymous types are marked private by the compiler and the properties are read-only. Is there a way to serialize them to xml (without deserialize) ? It works with JSON, how can I do it with XML?
我知道匿名类型被编译器标记为私有,并且属性是只读的。有没有办法将它们序列化为 xml(无需反序列化)?它适用于 JSON,我如何使用 XML 做到这一点?
采纳答案by John Saunders
It can't be accomplished using XmlSerializernor DataContractSerializer. It can be done by a manually written code, as demonstrated below(I can't comment as to whether the code is comprehensive enough to handle all types - but it's a very good start).
不能使用XmlSerializernor来完成DataContractSerializer。它可以通过手动编写的代码来完成,如下所示(我无法评论代码是否足够全面以处理所有类型 - 但这是一个非常好的开始)。
回答by Matthew Whited
Something like this should get you started...
这样的事情应该让你开始......
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;
}
}
... resulting xml ...
... 产生的 xml ...
<object>
<Hello>World</Hello>
<Other>
<My>Object</My>
<V>1</V>
<B>2</B>
</Other>
</object>
回答by ricardo
Thank you, excellent work @Matthew and @Martin.
谢谢@Matthew 和@Martin 的出色工作。
I have made a couple of modification to accomodate Nullables and Enums. Also I have changed it so that array elements are named according to the name of the property + index.
我进行了一些修改以适应 Nullables 和 Enums。我也改变了它,以便根据属性名称+索引来命名数组元素。
Here is the code if anyone is interested
如果有人感兴趣,这是代码
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
}
回答by VdesmedT
My own version of then excellent work @Matthew and @Martin : Arrays of enums are now supported and the notion of arrays in generalized into IEnumerable in order to also support all sort of collections.
我自己当时出色的作品 @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
}
回答by Pavel
I know this is an old post, but my solution converts an anonymous type to XML in only 2 lines of code.
我知道这是一篇旧帖子,但我的解决方案仅用2 行代码将匿名类型转换为 XML 。
First convert you anonymous type to JSON, and then from JSON to XML.
首先将您的匿名类型转换为 JSON,然后从 JSON 转换为 XML。
var jsonText = JsonConvert.SerializeObject(data); // convert to JSON
XmlDocument doc = JsonConvert.DeserializeXmlNode(jsonText); // convert JSON to XML Document
Sample
样本
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);
Output
输出
<Request>
<OrderNumber>123</OrderNumber>
<Note>Hello World</Note>
</Request>
回答by Jeremy Cook
The answer below handles IEnumerables in the way I needed and will turn this:
下面的答案以我需要的方式处理 IEnumerables 并将其转变为:
new
{
Foo = new[]
{
new { Name = "One" },
new { Name = "Two" },
},
Bar = new[]
{
new { Name = "Three" },
new { Name = "Four" },
},
}
into this:
进入这个:
<object>
<Foo><Name>One</Name></Foo>
<Foo><Name>Two</Name></Foo>
<Bar><Name>Three</Name></Bar>
<Bar><Name>Four</Name></Bar>
</object>
So here you go, yet another variant of Matthew's answer:
所以你走了,马修答案的另一种变体:
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;
}
}
回答by Gil
My first post to contribute to a website that always always helps a lot
Bellow is a full solution that can be used with .net core (tested using 3.1)
just call ConvertAnonymousToType.Convert("MyNewType", target, out Type newType)
The return will be an object that you can use to return inside a controller or to serialize manually (System.Xml.Serialization.XmlSerializer).
Using the object as a return Inside a controller:
1. configure the service (Startup.cs > ConfigureServices > services.AddMvcCore(options => options.OutputFormatters.Add(new XmlSerializerOutputFormatter());),
2. Target Action: use [FormatFilter] and [Route(".......{format}")],
3. Return the object
我为一个总是有很多帮助的网站做出贡献的第一篇文章是一个完整的解决方案,可以与 .net 核心一起使用(使用 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. Target Action: use [ FormatFilter] and [Route(".......{format}")], 3.返回对象
If any extra anonymous type needs to compose the reply (Metadata, Resultset, etc) DynamicTypeBuilder.CreateNewObject can be used to do service :)
如果任何额外的匿名类型需要撰写回复(元数据、结果集等),可以使用 DynamicTypeBuilder.CreateNewObject 来做服务:)
Thanks again
再次感谢
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;
}
}
}

