0

I have a class that calls a webservice which returns a JSON string which I need to deserialize into a C# object. I was successfully able to do this; however, I came across a scenario that I am not sure exactly the best way to handle. More specifically, the JSON will either return a List<List<object>> or just a List<object>. I am having a problem when I deserialize if my object is List<object> and the JSON is List<List<object>>. In that case, an exception is thrown.

This is class that I am trying to deserialize:

    public class WorkOrderJson
    {
        public string type { get; set; }
        public Properties properties { get; set; }
        public Geometry geometry { get; set; }
    }

    public class Properties
    {
        public string FeatureType { get; set; }
        public string WorkOrderID { get; set; }
        public string EqEquipNo { get; set; }
    }

For the Geometry class the coordinates returned are the issue from above. If the JSON returned is a List<List<object>> it serializes fine.

    public class Geometry
    {
        public string type { get; set; }
        public List<List<double>> coordinates { get; set; }
    }

This is how I am performing deserialization:

    WorkOrderJson workOrderJson = new JavaScriptSerializer().Deserialize<List<WorkOrderJson>>(responseString);

where responseString is the JSON string returned from web service. Hope this makes sense. If anybody has come across a similar issue, any help would be much appreciated.

Here is an example for List<List<object>> where coordinates is the list:

[
    {
        "type": "Feature",
        "properties": {
            "FeatureType": "WORKORDER",
            "WorkOrderID": "AMO172-2015-107",
            "EqEquipNo": "AC-LIN-001"
        },
        "geometry": {
            "type": "LineString",
            "coordinates": [
                [
                    -111.00041804208979,
                    33.0002148138019
                ],
                [
                    -111.00027869450028,
                    33.000143209356054
                ]
            ]
        },
        "symbology": {
            "color": "#990000",
            "lineWidth": "8"
        }
    }
]

Here is an example for List<object>:

[
    {
        "type": "Feature",
        "properties": {
            "FeatureType": "WORKORDER",
            "WorkOrderID": "AMO172-2015-115",
            "EqEquipNo": "AC-LIN-001"
        },
        "geometry": {
            "type": "Point",
            "coordinates": [
                -111.00041804208979,
                33.0002148138019
            ]
        }
    }
]
2
  • This tool is nice just paste your json in there. json2csharp.com Commented Sep 2, 2015 at 20:18
  • 1
    @vikingben Yes i used that to create the object for the above json. However i could be getting either message above and the class generated is a list(coordinate) for second post and list(list(coordinate)) for the first post Commented Sep 2, 2015 at 20:20

3 Answers 3

1

So for anyone that cares changing the Geometry Class to the following solved my problem:

public class Geometry
{
        public string type { get; set; }
        public object coordinates { get; set; }
}

Just changed list to object. Then at runtime i can check whether the object is list of lists or just a list and proceed.

Sign up to request clarification or add additional context in comments.

Comments

1

Create a custom JavaScriptConverter and register it with your JavaScriptSerializer.

You would then deserialize like this:

var converter = new JavaScriptSerializer();
converter.RegisterConverters(new List<JavaScriptConverter>() {new GeometryConverter()});
var workOrderJson = converter.Deserialize<List<WorkOrderJson>>(response);

This converter would work:

public class GeometryConverter : JavaScriptConverter
{

    public override IEnumerable<Type> SupportedTypes
    {
        get { return new List<Type>(new Type[] {typeof(Geometry)}); }
    }

    public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
    {
        Geometry geometry = obj as Geometry;

        if (geometry != null)
        {
            // Create the representation
            var result = new Dictionary<string, object>();

            if (geometry.coordinates.Count == 1)
            {
                result.Add("type", "Point");
                result.Add("coordinates", geometry.coordinates[0]);
            }
            else if (geometry.coordinates.Count > 1)
            {
                result.Add("type", "LineString");
                result.Add("coordinates", geometry.coordinates);
            }
            return result;
        }

        return new Dictionary<string, object>();
    }

    public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
    {
        if (dictionary == null)
                throw new ArgumentNullException("dictionary");

        Geometry geometry = null;
        if (type == typeof(Geometry))
        {
            geometry = new Geometry();
            geometry.type = (string)dictionary["type"];
            geometry.coordinates = new List<List<double>>();
            if ( geometry.type == "Point")
            {
                ArrayList arrayList = (ArrayList)dictionary["coordinates"];
                geometry.coordinates.Add(ConvertCoordinates(arrayList));
            }
            else if (geometry.type == "LineString")
            {
                geometry.type = "LineString";
                ArrayList coordinatesList = (ArrayList)dictionary["coordinates"];
                foreach (ArrayList arrayList in coordinatesList)
                {
                    geometry.coordinates.Add(ConvertCoordinates(arrayList));
                }
            }
        }
        return geometry;
    }

    private List<double> ConvertCoordinates(ArrayList coordinates)
    {
        var list = new List<double>();
        foreach (var coordinate in coordinates)
        {
            list.Add((double)System.Convert.ToDouble(coordinate));
        }
        return list;
    }
}

1 Comment

Thanks this looks like a great solution. My solution is already working so not gonna mess with it but this is much better.
0

If I understand and you are trying to deserialize a List<List<a>> as a List<a>, it should error. You're telling it to deserialize it into something other than what it was before serialization. I would recommend either propagating some indication of which it is along with the string so you can check and deserialize as that type, or wrap the deserialization attempts and try one first, then the other if it fails.

Edit per update

public class WorkOrderJson<T>
{
    public Geometry<T> geometry { get; set; }

    public WorkOrderJson<List<T>> Promote()
    {
        var temp = new WorkOrderJson<List<T>>();
        temp.geometry = geometry.Promote();
        return temp;
    }
}
public class Geometry<T>
{
    public T coordinates { get; set; }

    public Geometry<List<T>> Promote()
    {
        var temp = new Geometry<List<T>>();
        temp.coordinates = new List<T>(){ coordinates };
        return temp;
    }
}

public List<WorkOrder<List<List<double>>>> Deserialize(string x)
{
    try
    {
        return new JavaScriptSerializer().Deserialize<List<WorkOrderJson<List<List<double>>>>>(x);
    }
    catch(InvalidOperationException ex)
    {
        return new JavaScriptSerializer().Deserialize<List<WorkOrderJson<List<double>>>>(x).Select(workOrder => workOrder.Promote());
    }
}

4 Comments

Yes i agree and it should fail but the problem is that the json return string is "coordinates" for both list(list(object)) and list(object) so how would i create the Geometry class when i don't know if return is list(list(object)) or list(object). Question is how to handle both?
@Maxqueue I understand better with your update what the issue is. Looks like you found a resolution, but an alternative would be to parameterize WorkOrder (and Geometry) so it would be WorkOrder<List<List<double>>> or WorkOrder<List<double>>. try to deserialize as the first one, and if that fails deserialize as the second one. You would need to "promote" the List<double> case to List<List<double>> when returning (a list of one element), though, so they are the same type.
But then you would have two variables with same name.
@Maxqueue I updated the answer with an example of what I'm proposing. It's not pretty and whether it's worth it depends on your use case. But having a bare object floating around and possibly having to switch on whether it's a List or a List<List> everywhere isn't really appealing either.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.