1

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; }
    }
Orion77
  • 92
  • 9

3 Answers3

1

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.

e.g

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

AliReza Sabouri
  • 4,355
  • 2
  • 25
  • 37
  • 1
    i'm sorry but i have 13 of reputation i can't... – Orion77 Feb 11 '20 at 11:39
  • now i'm getting an error on list type Parameter count mismatch..i tryied to do with this command el.SetValue(newObj, elInstance,new object[]{0}); but the response was Index was out of range. Must be non-negative and less than the size of the collection. (Parameter 'index') – Orion77 Feb 17 '20 at 14:03
  • you can ignore list types if you want (just add another if condition for lists) or initialize lists like this: https://stackoverflow.com/a/38329808/786376 – AliReza Sabouri Feb 17 '20 at 14:35
0

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; }
    }
}
jdweng
  • 33,250
  • 2
  • 15
  • 20
  • Hi, the problemi is not the properties, the code above is a snippet of entire code that serialize the xml. My question is if i can instantiate all properties of envelope and so on, with a foreach loop. Thx you – Orion77 Feb 11 '20 at 07:10
  • What is the source of the data? What does the output of the xml look like? I usually use other methods to generate xml when doing a dynamic instantiate. For example using xml linq you can generate a new element using : XElement newNode = new XElement("Name", "Value"); where the Name and Value are string. – jdweng Feb 11 '20 at 10:00
0

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

Orion77
  • 92
  • 9