0

I have 3 classes which form an object like this :

public class Pax
{
    public PaxType PaxType { get; set; }
    public int Age { get; set; }
    public string Title { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

public class Room
    {
        public Room()
        {
            Paxes = new List<Pax>();
        }

        public List<Pax> Paxes { get; set; }
    }

public class AvaliableHotelRequest
{
    public AvaliableHotelRequest()
    {
        Rooms = new List<Room>();
    }

    public string Method { get; set; }
    public string ApiKey { get; set; }
    public string DestinationId { get; set; }
    public DateTime CheckIn { get; set; }
    public DateTime CheckOut { get; set; }
    public string Currency { get; set; }
    public string ClientNationality { get; set; }
    public bool OnRequest { get; set; }
    public List<Room> Rooms { get; set; }
}

I need to use reflection to get following object in to an string like this:

        var pax1 = new Pax()
        {
            PaxType = PaxType.Adult
        };

        var pax2 = new Pax()
        {
            PaxType = PaxType.Adult
        };

        var pax3 = new Pax()
        {
            PaxType = PaxType.Children,
            Age = 5
        };

        var paxList1 = new List<Pax> {pax1, pax2, pax3};
        var paxList2 = new List<Pax> { pax2, pax3 };
        var rooms = new List<Room>(){new Room(){Paxes = paxList1}, new Room(){Paxes = paxList2}};


        var request = new AvaliableHotelRequest()
        {
            ApiKey = "jhjfjdshsfjkhjhfsdfks",
            CheckIn = DateTime.Now,
            CheckOut = DateTime.Now.AddDays(5),
            ClientNationality = "AU",
            Currency = "EUR",
            DestinationId = "LD6J",
            Method = "getAvailableHotel",
            OnRequest = false,
            Rooms = rooms
        };

output : ?method=getAvailableHotel&apiKey=kggdjjgdhgkjghkgjghkjdg&destinationId=LD6J&checkIn=2011-04-20&checkOut=2011-04-24&currency=EUR&clientNationality=UK&onRequest=false&rooms[0][0][paxType]=Adult&rooms[0][1][paxType]=Adult&rooms[0][2][paxType]=Child&rooms[0][2][age]=6&rooms[1][0][paxType]=Adult&rooms[1][1][paxType]=Adult&rooms[1][2][paxType]=Child&rooms[1][2][age]=8

Been trying different ways but could not get passed this issue. I would be thankful if you help me.

I found this Recursively Get Properties & Child Properties Of An Object but its just listing the properties not the values. Thanks.

Community
  • 1
  • 1
Alex
  • 274
  • 5
  • 15
  • Are you sure that reflection is what you want? Don't you have a finite small number of types in your hierarchy? – galenus Oct 27 '13 at 13:00
  • Well this is one of types I need to convert there are 5-6 more types with different properties. Is there any better way than reflection to serialize an object and its properties to query-string like that? – Alex Oct 27 '13 at 13:08
  • I would create a common interface, something like ISerializable, but also a method, returning a list of children. This way, it will be really easy to write a recursive function serializing the whole graph. – galenus Oct 27 '13 at 13:13
  • how do you use the property named Method,where and in what class and whats the method like please,because i guess its from that string you want to get info...and where is and whats getAvailableHotel like? – terrybozzio Oct 27 '13 at 14:32

2 Answers2

1

Another option would be to define a custom serialization mechanism.

For example, consider the following implementation (that has some rough edges and does not necessary cover corner cases). The following interface defines serialization of some object as query part:

public interface IQueryPartDescriptor
{
    /// <summary>
    /// Converts the specified object into a string usable as query part.
    /// </summary>
    string ObjectToQueryPart(string prefix, object obj);

    /// <summary>
    /// Describes the properties containing lists of children objects.
    /// </summary>
    IEnumerable<ChildrenCollectionDescriptor> ChildrenListsDescriptors { get; }
}

With children collection descriptor like:

public struct ChildrenCollectionDescriptor
{
    private readonly Func<object, IEnumerable<object>> _getChildren;
    private readonly Func<int, string, string> _buildChildPrefix;

    public ChildrenCollectionDescriptor(
        Func<object, IEnumerable<object>> getChildren,
        Func<int, string, string> buildChildPrefix)
        : this()
    {
        _getChildren = getChildren;
        _buildChildPrefix = buildChildPrefix;
    }

    public IEnumerable<object> GetChildren(object parent)
    {
        return _getChildren(parent);
    }

    public string BuildChildPrefix(int childPosition, string accumulatedPrefix)
    {
        return _buildChildPrefix(childPosition, accumulatedPrefix);
    }
}

Implementations of this interface for each of the classes you've described will look something like:

public class PaxDescriptor : IQueryPartDescriptor
{
    public string ObjectToQueryPart(string prefix, object obj)
    {
        var pax = (Pax)obj;
        var queryPart = prefix + "[paxType]=" + pax.PaxType;
        if (pax.PaxType == PaxType.Child)
        {
            queryPart += prefix + "[age]=" + pax.Age;
        }

        return queryPart;
    }

    public IEnumerable<ChildrenCollectionDescriptor> ChildrenListsDescriptors
    {
        get { return Enumerable.Empty<ChildrenCollectionDescriptor>(); }
    }
}

public class RoomDescriptor : IQueryPartDescriptor
{
    public string ObjectToQueryPart(string prefix, object obj)
    {
        return String.Empty;
    }

    public IEnumerable<ChildrenCollectionDescriptor> ChildrenListsDescriptors
    {
        get
        {
            return new[]
            {
                new ChildrenCollectionDescriptor(
                    room => ((Room)room).Paxes,
                    (index, roomsPrefix) => roomsPrefix + "[" + index + "]")
            };
        }
    }
}

public class AvaliableHotelRequestDescriptor : IQueryPartDescriptor
{
    public string ObjectToQueryPart(string prefix, object obj)
    {
        var request = (AvaliableHotelRequest)obj;
        return
            "method=" + request.Method + "&" +
            "apiKey=" + request.ApiKey + "&" +
            "destinationID=" + request.DestinationId + "&" +
            "checkIn=" + request.CheckIn.ToString("yyyy-MM-dd") + "&" +
            "checkOut=" + request.CheckOut.ToString("yyyy-MM-dd") + "&" +
            "currency=" + request.Currency + "&" +
            "clientNationality=" + request.ClientNationality + "&" +
            "onRequest=" + request.OnRequest.ToString().ToLower();
    }

    public IEnumerable<ChildrenCollectionDescriptor> ChildrenListsDescriptors
    {
        get
        {
            return new[]
            {
                new ChildrenCollectionDescriptor(
                    request => ((AvaliableHotelRequest)request).Rooms,
                    (index, _) => "&rooms[" + index + "]")
            };
        }
    }
}

Of course, implementations of the interface could be provided by the classes in question themselves.

Now, the whole serialization part will be:

public static string ToQuery(object root, IDictionary<Type, IQueryPartDescriptor> partDescriptors)
{
    var queryBuilder = new StringBuilder();
    AddQueryPart(root, String.Empty, partDescriptors, queryBuilder);

    return queryBuilder.Insert(0, '?').ToString();
}

private static void AddQueryPart(
    object obj,
    string prefixInQuery,
    IDictionary<Type, IQueryPartDescriptor> partDescriptors,
    StringBuilder queryBuilder)
{
    var queryPartDescriptor = partDescriptors[obj.GetType()];

    queryBuilder
        .Append(queryPartDescriptor.ObjectToQueryPart(prefixInQuery, obj));

    foreach (var childrenListDescriptor in queryPartDescriptor.ChildrenListsDescriptors)
    {
        var children = childrenListDescriptor.GetChildren(obj).ToList();
        for (var childIndex = 0; childIndex < children.Count; childIndex++)
        {
            var childPrefix = childrenListDescriptor.BuildChildPrefix(childIndex, prefixInQuery);
            AddQueryPart(children[childIndex], childPrefix, partDescriptors, queryBuilder);
        }
    }
}

This approach does not involve reflection and is more modular. You can also easily extend the number of the covered types by adding more descriptors.

galenus
  • 2,087
  • 16
  • 24
-1

Here's an idea. reflection here? shows where you could apply reflection. I that case you could reflect all properties of Room or Pax and set the parameter with this information instead of hardcoding it.

List<string> parameters = new List<string>();

// reflection here?
parameters.Add("ApiKey=" + request.ApiKey);
// ... more request parameters here

for (int i = 0; i < request.Rooms.Count; i++)
{
    Room room = request.Rooms[i];

    for (int k = 0; room.Paxes.Count; k++)
    {
        Pax pax = room.Paxes[k];
        string roomParam = "room[" + i + "][" + k + "]";

        // reflection here?
        parameters.Add(roomParam + "[age]=" + pax.Age);
        // ... more pax parameters
    }
}

// we join all parameters get the query string
string query = "?" + String.Join("&", parameters)
Knickedi
  • 8,742
  • 3
  • 43
  • 45