Deserializing JSON (without dynamic) where array has multiple possible depths based on value of another property?

huangapple go评论76阅读模式
英文:

Deserializing JSON (without dynamic) where array has multiple possible depths based on value of another property?

问题

You are on the right track with your custom JsonConverter approach to handle the varying depth of the coordinates array based on the Geometry.type. However, the error you're encountering, "InvalidCastException: Cannot cast Newtonsoft.Json.Linq.JArray to Newtonsoft.Json.Linq.JToken," suggests that there might be an issue with how you are trying to access and deserialize the coordinates array.

Here's a potential solution:

class GeometryConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return (objectType == typeof(Geometry));
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        Geometry geometry = new Geometry();

        JObject jo = JObject.Load(reader);
        geometry.type = jo["type"].Value<string>();

        if (geometry.type == "Polygon")
        {
            geometry.coordinatesPolygon = jo["coordinates"]
                .ToObject<List<List<List<List<double>>>>>(); // Deserialize as List of List of List of List of double
        }
        else if (geometry.type == "MultiPolygon")
        {
            geometry.coordinatesMultipolygon = jo["coordinates"]
                .ToObject<List<List<List<List<List<double>>>>>>(); // Deserialize as List of List of List of List of List of double
        }

        return geometry;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        // If you want to support serializing, you could implement this method as well
        throw new NotImplementedException();
    }
}

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

public class Properties
{
    public string type { get; set; }
    public string name { get; set; }
}

public class RegionJsonObject
{
    public string type { get; set; }
    public string id { get; set; }
    public Properties properties { get; set; }

    [JsonProperty("geometry")]
    [JsonConverter(typeof(GeometryConverter))]
    public Geometry geometry { get; set; }
}

This code uses the ToObject method to directly deserialize the coordinates array into the appropriate list object based on the Geometry.type. Make sure to update your code accordingly, and it should work as intended.

英文:

I am trying to deserialize the following JSON without using dynamic as it is not supported by my system: https://raw.githubusercontent.com/vansha/Two10.CountryLookup/master/Two10.CountryLookup/region-list.json

Each line presents a region for geolocating with.

Using the JSON Converter here: https://json2csharp.com/

I get the suggestion for data classes as:

    // Root myDeserializedClass = JsonConvert.DeserializeObject&lt;RegionJsonObject&gt;(myJsonResponse);

    public class Geometry {
        public string type { get; set; }
        public List&lt;List&lt;List&lt;List&lt;double&gt;&gt;&gt;&gt; coordinates { get; set; }
    }

    public class Properties {
        public string name { get; set; }
        public string type { get; set; }
    }

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

The problem is that if you look at the JSON, there are actually two possible formats for the coordinates array. If the Geometry.type is Polygon we have three levels of depth [[[ ]]] and if it is MultiPolygon we get four levels of depth [[[[ ]]]]. So I am instead considering a format of the following with a custom JsonConverter:

    class GeometryConverter : JsonConverter {
        public override bool CanConvert(Type objectType) {
            return (objectType == typeof(Geometry));
        }

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {

            Geometry geometry = new Geometry();

            JObject jo = JObject.Load(reader);
            geometry.type = jo[&quot;type&quot;].Value&lt;string&gt;();

            if (geometry.type == &quot;Polygon&quot;) {
                geometry.coordinatesPolygon = jo[&quot;coordinates&quot;].Value&lt;List&lt;List&lt;List&lt;double&gt;&gt;&gt;&gt;(); //DOESN&#39;T WORK
            }
            else {
                geometry.coordinatesMulipolygon = jo[&quot;coordinates&quot;].Value&lt;List&lt;List&lt;List&lt;List&lt;double&gt;&gt;&gt;&gt;&gt;(); //DOESN&#39;T WORK
            }

            return geometry;
        }

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
            // If you want to support serializing you could implement this method as well
            throw new NotImplementedException();
        }
    }

    public class Geometry {
        public string type { get; set; }
        public List&lt;List&lt;List&lt;List&lt;double&gt;&gt;&gt;&gt; coordinatesMulipolygon { get; set; } //multipolygon
        public List&lt;List&lt;List&lt;double&gt;&gt;&gt; coordinatesPolygon { get; set; } //polygon
    }

    public class Properties {
        public string type { get; set; }
        public string name { get; set; }
    }

    public class RegionJsonObject {
        public string type { get; set; }
        public string id { get; set; }
        public Properties properties { get; set; }

        [JsonProperty(&quot;geometry&quot;)]
        [JsonConverter(typeof(GeometryConverter))] 
        public Geometry geometry { get; set; }
    }

}

However, this is not working. Trying to set geometry.coordinatesPolygon or geometry.coordinatesMultipolygon in this manner gives the error:

InvalidCastException: Cannot cast Newtonsoft.Json.Linq.JArray to Newtonsoft.Json.Linq.JToken.

Am I on the right track with this approach? How do I get the arrays from the JSON into each of these two list objects as needed? Is there a better or more proper way?

Thanks for any help.

答案1

得分: 1

你可以考虑在你的几何图形中使用多态性:一个类 Polygon 和一个类 Multipolygon
然后你可以使用 JsonSubTypes 转换器。

你还有一个 Stack Overflow 帖子 这里

英文:

You should maybe use polymorphism for your geometry : one class Polygon and one class Multipolygon.
Then you can use JsonSubTypes converter.

You have also a stackoverflow post here

答案2

得分: 1


            if (geometry.type == "Polygon") {
                geometry.coordinatesPolygon = jo["coordinates"].ToObject<List<List<List<double>>>>(); //工作正常
            }
            else {
                geometry.coordinatesMulipolygon = jo["coordinates"].ToObject<List<List<List<List<double>>>>>(); //工作正常
            }

这解决了问题。

英文:

            if (geometry.type == &quot;Polygon&quot;) {
                geometry.coordinatesPolygon = jo[&quot;coordinates&quot;].ToObject&lt;List&lt;List&lt;List&lt;double&gt;&gt;&gt;&gt;(); //WORKS
            }
            else {
                geometry.coordinatesMulipolygon = jo[&quot;coordinates&quot;].ToObject&lt;List&lt;List&lt;List&lt;List&lt;double&gt;&gt;&gt;&gt;&gt;(); //WORKS
            }

This solved it.

huangapple
  • 本文由 发表于 2023年6月29日 15:43:50
  • 转载请务必保留本文链接:https://go.coder-hub.com/76578994.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定