简体   繁体   中英

Instantiate all class objects serialized from xml

I have deserialized a XML file into a class that make my soap request object. When I'm serializing C# class most of classes object not fill the output xml file.

Example

GetUserReq.Envelope getUser = new GetUserReq.Envelope();
getUserResponse = new GetUserRes.Envelope();
getUser.Body = new GetUserReq.Body();
getUser.Body.GetUser = new GetUserReq.GetUser();
getUser.Body.GetUser.ReturnedTags = new GetUserReq.ReturnedTags();

if (allReturnTags)
{
    getUser.Body.GetUser.ReturnedTags.AssociatedGroups = new GetUserReq.AssociatedGroups();
    getUser.Body.GetUser.ReturnedTags.AssociatedDevices = new GetUserReq.AssociatedDevices();
    getUser.Body.GetUser.ReturnedTags.AssociatedGroups.UserGroup = new GetUserReq.UserGroup() { Name = "", UserRoles = new GetUserReq.UserRoles() };
    getUser.Body.GetUser.ReturnedTags.AssociatedGroups.UserGroup.UserRoles = new GetUserReq.UserRoles() { UserRole = "" };
}

For each item nested in the "envelope", I need to create new object otherwise the output xml file will be empty by that tag.

There are any method could do a iteration and made what I need?

These is a snippet code where start Envelope

public class GetUserReq {
[XmlRoot(ElementName = "Envelope", Namespace = "http://schemas.xmlsoap.org/soap/envelope/")]
        public class Envelope
        {
            [XmlElement(ElementName = "Header", Namespace = "http://schemas.xmlsoap.org/soap/envelope/")]
            public string Header { get; set; }
            [XmlElement(ElementName = "Body", Namespace = "http://schemas.xmlsoap.org/soap/envelope/")]
            public Body Body { get; set; }
            [XmlAttribute(AttributeName = "soapenv", Namespace = "http://www.w3.org/2000/xmlns/")]
            public string Soapenv { get; set; }
            [XmlAttribute(AttributeName = "ns", Namespace = "http://www.w3.org/2000/xmlns/")]
            public string Ns { get; set; }
        }

and go on with body that contains other classes

[XmlRoot(ElementName = "Body", Namespace = "http://schemas.xmlsoap.org/soap/envelope/")]
    public class Body
    {
        [XmlElement(ElementName = "getUser", Namespace = "http://www.cisco.com/AXL/API/9.1")]
        public GetUser GetUser { get; set; }
    }

You were only defining classes and did not have the properties for the classes. See code below :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;

namespace ConsoleApplication1
{
    class Program
    {
        const string FILENAME = @"c:\temp\test.xml";
        static void Main(string[] args)
        {
            GetUserReq userReq = new GetUserReq();
            userReq.Envelope = new Envelope();
            GetUserResponse userResponse = new GetUserResponse();
            userResponse.Envelope = new Envelope();
            userReq.Body = new Body();
            userReq.Body.GetUser = new GetUser();
            userReq.Body.GetUser.ReturnedTags = new ReturnedTags();

            Boolean allReturnTags = true;
            if (allReturnTags)
            {
                userReq.Body.GetUser.ReturnedTags.AssociatedGroups = new AssociatedGroups();
                userReq.Body.GetUser.ReturnedTags.AssociatedDevices = new AssociatedDevices();
                userReq.Body.GetUser.ReturnedTags.AssociatedGroups.UserGroup = new UserGroup() { Name = "", UserRoles = new UserRoles() };
                userReq.Body.GetUser.ReturnedTags.AssociatedGroups.UserGroup.UserRoles = new UserRoles() { UserRole = "" };
            }

            XmlWriterSettings settings = new XmlWriterSettings();
            settings.Indent = true;
            XmlWriter writer = XmlWriter.Create(FILENAME, settings);

            XmlSerializer serializer = new XmlSerializer(typeof(GetUserReq));
            serializer.Serialize(writer, userReq);
        }
    }
    [XmlRoot(ElementName = "Body", Namespace = "http://schemas.xmlsoap.org/soap/envelope/")]
    public class Body
    {
        [XmlElement(ElementName = "getUser", Namespace = "http://www.cisco.com/AXL/API/9.1")]
        public GetUser GetUser { get; set; }
    }
    public class GetUser
    {
        public ReturnedTags ReturnedTags { get; set; }
    }
    public class ReturnedTags
    {
        public AssociatedGroups AssociatedGroups { get; set; }
        public AssociatedDevices AssociatedDevices { get; set; }
    }
    public class GetUserReq
    {
        public Envelope Envelope { get; set; }
        public Body Body { get; set; }

    }
    public class GetUserResponse
    {
        public Envelope Envelope { get; set; }

    }
    [XmlRoot(ElementName = "Envelope", Namespace = "http://schemas.xmlsoap.org/soap/envelope/")]
    public class Envelope
    {
        [XmlElement(ElementName = "Header", Namespace = "http://schemas.xmlsoap.org/soap/envelope/")]
        public string Header { get; set; }
        [XmlElement(ElementName = "Body", Namespace = "http://schemas.xmlsoap.org/soap/envelope/")]
        public Body Body { get; set; }
        [XmlAttribute(AttributeName = "soapenv", Namespace = "http://www.w3.org/2000/xmlns/")]
        public string Soapenv { get; set; }
        [XmlAttribute(AttributeName = "ns", Namespace = "http://www.w3.org/2000/xmlns/")]
        public string Ns { get; set; }
    }
    public class AssociatedGroups
    {
        public UserGroup UserGroup { get; set; }
    }
    public class AssociatedDevices
    {
    }
    public class UserGroup
    {
        public UserRoles UserRoles { get; set; }
        public string Name { get; set; }
    }
    public class UserRoles
    {
        public string UserRole { get; set; }
    }
}

You can use reflection.


public object CascadeInitializer(Type type)     
{
    var newObj = Activator.CreateInstance(type); // create new instance of your target class
    Func<PropertyInfo,bool> query = q
        => q.PropertyType.IsClass &&            // Check if property is a class
           q.CanWrite &&                        // Check if property is not readOnly
           q.PropertyType != typeof(string);    // Check if property is not string

    foreach (var el in type.GetProperties().Where(query))
    {
        // create new instance of el cascade
        var elInstance = CascadeInitializer(el.PropertyType);
        el.SetValue(newObj, elInstance);
    }
    return newObj;
}

// a generic overload to easier usage
public T CascadeInitializer<T>() => (T)CascadeInitializer(typeof(T));

usage

var x =  CascadeInitializer<Envelope>();

also if you want to control what classes should be automatically initialized, you can add an empty interface interface IInitializable to your classes, this way you can check what property is of IInitializable type in the Func query .

eg

Func<PropertyInfo,bool> query = q
    => q.PropertyType.IsClass &&            // Check if property is a class
       q.CanWrite &&                        // Check if property is not readOnly
       q.PropertyType != typeof(string) &&  // Check if property is not string
       q.PropertyType.GetInterfaces()       // Check what classes should be initialize 
           .Any(i => i.Name == nameof(IInitializable) );

...
public interface IInitializable{}

public class Envelope : IInitializable {
.....


test on dotnetfiddle : https://dotnetfiddle.net/Xm8nEX

I did this code made from your example and modified for my neededs

    public T AllReturnTags<T>() => (T)AllReturnTags(typeof(T));

    public object AllReturnTags(Type type)
    {
        var newObj = Activator.CreateInstance(type); // create new instance of your target class

        Func<PropertyInfo, bool> query = q
             => q.PropertyType.IsClass &&         
                q.CanWrite;                  

        foreach (var el in type.GetProperties().Where(query))
        {
            // create new instance of el cascade
            if (el.PropertyType == typeof(string))
            {
                el.SetValue(newObj, "", null);
            }
            if (el.PropertyType == typeof(Int32))
            {
                el.SetValue(newObj, 0, null);
            }
            if (el.PropertyType.IsClass && el.PropertyType != typeof(string) && el.PropertyType != typeof(Int32) && el.PropertyType.IsGenericType == true && el.PropertyType.GetGenericTypeDefinition() == typeof(List<>))
            {
                var elInstance = AllReturnTags(el.PropertyType);
                Type itemType = typeof(List<>).MakeGenericType(elInstance.GetType());
                IList res = (IList)Activator.CreateInstance(itemType);
                res.Add(elInstance);
                try { el.SetValue(newObj, res, null); } catch {  };
            }
            if (el.PropertyType.IsClass && el.PropertyType != typeof(string) && el.PropertyType != typeof(Int32) && el.PropertyType.IsGenericType != true )
            {
                var elInstance = AllReturnTags(el.PropertyType);
                try { el.SetValue(newObj, elInstance, null); } catch { return elInstance; };
            }

        }
        return newObj;
    }

This seems to work with single items and lists. Thx you @AliReza

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM