60 votes

Puis-je sérialiser des types anonymes en xml ?

J'ai compris que les types anonymes sont marqués comme privés par le compilateur et que les propriétés sont en lecture seule. Y a-t-il un moyen de les sérialiser en XML (sans désérialisation) ? Ça fonctionne avec JSON, comment puis-je le faire avec XML ?

2voto

Jeremy Cook Points 2236

La réponse ci-dessous gère les IEnumerables de la manière dont j'avais besoin et transformera ceci :

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

en ceci :

    One
    Two
    Three
    Four

Donc voilà, encore une autre variante de la réponse de Matthew :

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

0voto

Un autre utilisateur Pavel a mentionné l'utilisation des méthodes Newtonsoft.Json pour une conversion rapide en 2 lignes

var jsonText = JsonConvert.SerializeObject(data);           
XmlDocument doc = JsonConvert.DeserializeXmlNode(jsonText);

Je suggèrerais que cela pourrait en fait être une seule ligne de code :)

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

XmlDocument doc = JsonConvert.DeserializeXmlNode(JsonConvert.SerializeObject(data));

-1voto

Gil Points 1

Mon premier article pour contribuer à un site web qui aide toujours beaucoup Ci-dessous se trouve une solution complète qui peut être utilisée avec .net core (testée avec la version 3.1) appelez simplement ConvertAnonymousToType.Convert("MyNewType", target, out Type newType) Le retour sera un objet que vous pouvez utiliser pour renvoyer à l'intérieur d'un contrôleur ou pour le sérialiser manuellement (System.Xml.Serialization.XmlSerializer). Utilisation de l'objet comme retour à l'intérieur d'un contrôleur : 1. configurez le service (Startup.cs > ConfigureServices > services.AddMvcCore(options => options.OutputFormatters.Add(new XmlSerializerOutputFormatter());),
2. Action cible : utilisez [FormatFilter] et [Route(".......{format}")], 3. Renvoyez l'objet

Si un type anonyme supplémentaire doit composer la réponse (Metadata, Resultset, etc.), DynamicTypeBuilder.CreateNewObject peut être utilisé pour le service :)

Merci encore

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 properties, out Type newType)
        {
            newType = CompileResultType(typeName, properties);
            return Activator.CreateInstance(newType);
        }

        public static Type CompileResultType(string typeName, Dictionary properties)
        {
            TypeBuilder tb = GetTypeBuilder(typeName);
            ConstructorBuilder constructor = tb.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName);

            //Ajouter des propriétés
            properties.ToList().ForEach(p => CreateProperty(tb, p.Key, p.Value));

            //Type créé avec des propriétés
            Type objectType = tb.CreateType();
            return objectType;
        }

        //Créer un type avec une configuration standard
        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)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 properties = new Dictionary();
            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 GetProperties(object target)
        {
            object objectRef = target;
            if (target.GetType().Name == typeof(List<>).Name) objectRef = ((List)target).ToList()[0];

            Dictionary properties = new Dictionary();
            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;
        }
    }
}

-1voto

Douglas Marttinen Points 310

J'ai trouvé cette page extrêmement utile. Je passe des anciennes références web SOAP à quelque chose de pris en charge dans .net standard. J'ai modifié le code pour pouvoir le sérialiser dans un format qui fonctionnera avec les services web SOAP. C'est rudimentaire, mais cela peut convertir les éléments object[] en éléments , les autres tableaux simples sont correctement sérialisés et vous pouvez spécifier un espace de noms ou utiliser l'attribut XmlTypeAttribute sur vos modèles pour attribuer des espaces de noms.

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 SerializedTypeNames = new Dictionary()
        {
            { typeof(int), "int" },
            { typeof(long), "long" }
        };

        /// 
        /// Convertit un type anonyme en XElement.
        /// 
        /// L'entrée.
        /// Renvoie l'objet sous sa forme XML dans un XElement.
        public static XElement? ToXml(this object? input) => input.ToXml(null);

        /// 
        /// Convertit un type anonyme en XElement.
        /// 
        /// L'entrée.
        /// Le nom de l'élément.
        /// Renvoie l'objet sous sa forme XML dans un XElement.
        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(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;
        }

        /// 
        /// Obtient l'élément de tableau.
        /// 
        /// Les informations sur la propriété.
        /// L'objet d'entrée.
        /// Renvoie un XElement avec la collection du tableau en tant qu'éléments enfants.
        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(true);
                    var valNs = ns;

                    if (xmlType != null && xmlType.Namespace != null)
                        valNs = xmlType.Namespace;

                    // Créer l'élément anyType
                    var childElement = new XElement(ns + "anyType", new XAttribute(XNamespace.Xmlns + $"p{rootElement.Elements().Count()}", valNs));

                    // Créer l'attribut de type
                    string nsPrefix = childElement.GetPrefixOfNamespace(valNs);
                    childElement.Add(new XAttribute(_xmlInstanceNs + "type",
                        !string.IsNullOrEmpty(nsPrefix)
                            ? $"{nsPrefix}:{valType.Name}"
                            : valType.Name));

                    // Créer les éléments enfants
                    var inner = _ToXml(val, null, ns, i, "anyType");
                    childElement.Add(inner!.Elements());

                    // C'est terminé
                    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);
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X