Я понял, что анонимные типы помечаются компилятором как закрытые, а свойства доступны только для чтения. Есть ли способ сериализовать их в xml (без десериализации)? Он работает с JSON, как я могу сделать это с помощью XML?
Могу ли я сериализовать анонимные типы как xml?
Ответы (9)
Этого нельзя добиться ни с помощью XmlSerializer
, ни DataContractSerializer
. Это можно сделать с помощью кода, написанного вручную, как показано ниже (я не могу прокомментировать, является ли код исчерпывающим. достаточно, чтобы справиться со всеми типами - но это очень хорошее начало).
Что-то вроде этого должно вас начать ...
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>
Type.IsValueType
может быть хорошим ярлыком для большинства IsAssignableFrom
s. Однако не ловит string
.
- person sunside; 16.08.2010
IsValueType
может быть неправильным выбором. Это будет использовать значение ToString для преобразования типа. Но я внес изменение, которое должно быть намного легче понять.
- person Matthew Whited; 16.08.2010
Спасибо, отличная работа @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
}
Я знаю, что это старый пост, но мое решение преобразует анонимный тип в XML всего за 2 строки кода.
Сначала преобразуйте анонимный тип в 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>
Моя собственная версия тогдашней прекрасной работы @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
}
В приведенном ниже ответе 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;
}
}
Моя первая публикация, посвященная веб-сайту, который всегда очень помогает. Bellow - это полное решение, которое можно использовать с ядром .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. Целевое действие : используйте [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;
}
}
}
Я нашел эту страницу чрезвычайно полезной. Я перехожу от старых веб-ссылок SOAP к тому, что поддерживается стандартом .net. Я изменил код, чтобы иметь возможность сериализовать его в формате, который будет работать с веб-службами SOAP. Это грубо, но он может преобразовывать object[]
в <anyType />
элементы, другие простые массивы сериализованы правильно, и вы можете передать пространство имен или использовать XmlTypeAttribute
в ваших моделях для назначения пространств имен.
public static class AnonymousTypeSerializer
{
private static 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)
{
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, XNamespace ns = null)
{
return _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);
XElement 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;
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), 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)
{
var name = XmlConvert.EncodeName(info.Name);
var arrayCount = input == null ? 0 : input.GetLength(0);
var elementType = input.GetType().GetElementType();
var 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++)
{
var val = input.GetValue(i);
if (isAnyType)
{
var valType = val.GetType();
var xmlType = valType.GetCustomAttribute<XmlTypeAttribute>(true);
XNamespace 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
var 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 var 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)
{
return type.IsPrimitive || WriteTypes.Contains(type);
}
}
Другой постер Павел упомянул об использовании методов 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));