5

私の問題は、json オブジェクトを C# オブジェクトにデシリアライズしたいのですが、トリックは C# オブジェクトに List< abstract class > が含まれており、この抽象クラスが別の 10 クラスのスーパー クラスであることです。

public sealed class SearchAPIResult
{
    public string Status;

    public SearchAPIQuery Query;

    public SearchResults Result;

    public SearchAPIResult()
    {

    }

    public SearchAPIResult(string status)
    {
        Status = status;
    }
}

そしてSearchAPIResult次のとおりです。

public sealed class SearchResults
{
    public string TextAnswer;

    public List<APIResultWidget> Items;

    public SearchResults()
    {
        Items = new List<APIResultWidget>();
    }
}

ここで、オブジェクトAPIResultWidgetは抽象クラスであり、そこから約 10 個のクラスが継承されます。

問題は、JSON オブジェクトに、デシリアライザーを 10 個の派生クラスのどのオブジェクトにキャストするかをガイドするための自動化されたもの (JSON.NET の typeNameHandling など) がないことです。代わりに、オブジェクトは次のように Type と SubType の 2 つのフィールドでマークされます。

{
    "Status": "OK",
    "Query": {
        "Query": "this is a query",
        "QueryLanguage": "EN"
    },
    "Result": {
        "TextAnswer": "This is your text answer",
        "Items": [{
                "Type": "list",
                "SubType": null,
                "Title": null,
                "Items": []
            }, {
                "Type": "text",
                "Content": "this is some content"
            }
        ]
    }
}

前の json オブジェクトでは、Result リストに 2 つのオブジェクトが含まれています。

{
    "Type": "list",
    "SubType": null,
    "Title": null,
    "Items": []
}

これは listWidget 型のクラスにマップされます (abstractAPIResultWidgetと 2 つから継承します:

{
    "Type": "text",
    "Content": "this is some content"
}

textWidgetこれは、同じ抽象クラスからも継承するクラスにマップされます

Json.NET の方法を使用する場合

SearchAPIResult converted = (SearchAPIResult)JsonConvert.DeserializeObject(json, typeof(SearchAPIResult), new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto });

次の例外がスローされます。

タイプ Kngine.API.APIResultWidget のインスタンスを作成できませんでした。型はインターフェイスまたは抽象クラスであり、インスタンス化できません。パス 'Result.Items[0].Type'、行 1、位置 136。

その型がフィールド Type と SubType の両方によって定義されていることを指摘し、コンバーターにそのカスタム型アノテーターを与えるカスタムの方法があると思いますが、そうですか?

4

3 に答える 3

2

実際、私が実装したソリューションは非常に基本的なソリューションです。System.Web.Extentions から JavaScriptConverter クラスを拡張し、deserialize メソッドを実装しました。このメソッドは、元のオブジェクト内の各小さな json オブジェクトを Dictionary として自動的に受け取ります。オブジェクト自体に応じてフィールドを変更します。これは少し手動の方法ですが、それが私が思いついて機能する唯一の解決策でした。カスタム クラスの実装は次のようになります。

class myCustomResolver  : JavaScriptConverter

{
    public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
    {
        if (type == typeof(APIResultWidget))
        {
            switch ((string)dictionary["Type"])
            {
                case "weather":
                    {
                        WeatherWidget x = new WeatherWidget();
                        x.Location = (string)dictionary["Location"];
                        x.Current = (CurrentWeather)dictionary["Current"];
                        //x.Forcast = (List<WeatherForcastItem>)dictionary["Forcast"];

                        System.Collections.ArrayList itemss = ((System.Collections.ArrayList)dictionary["Forcast"]);
                        foreach (var item in itemss)
                        {
                            x.Forcast.Add(serializer.ConvertToType<WeatherForcastItem>(item));
                        }

                        return x;
                    };
                case "text":
                    {
                        TextWidget x = new TextWidget();
                        x.Content = (string)dictionary["Content"];
                        return x;
                    }; 
                case "keyValueText":
                    {
                        KeyValueTextWidget x = new KeyValueTextWidget();
                        x.Key = (string)dictionary["Key"];
                        x.Key = (string)dictionary["Value"];
                        x.Key = (string)dictionary["ValueURL"];
                        return x;
                    };
                case "keyValuesText":
                    {
                        KeyValuesTextWidget x = new KeyValuesTextWidget();
                        x.Key = (string)dictionary["Key"];
                        //x.Values = (List<ValueItem>)dictionary["ValueItem"];

                        System.Collections.ArrayList itemss = ((System.Collections.ArrayList)dictionary["ValueItem"]);
                        foreach (var item in itemss)
                        {
                            x.Values.Add(serializer.ConvertToType<ValueItem>(item));
                        }


                        return x;


                    }; 
                case "url":
                    {
                        URLWidget x = new URLWidget();
                        x.ThumbnailImageURL = (string)dictionary["ThumbnailImageURL"];
                        x.Title = (string)dictionary["Title"];
                        x.URL = (string)dictionary["URL"];
                        x.HTMLContent = (string)dictionary["HTMLContent"];
                        return x;

                    }; 
                case "map":
                    {
                        MapWidget x = new MapWidget();
                        System.Collections.ArrayList itemss = ((System.Collections.ArrayList)dictionary["Pins"]);
                        foreach (var item in itemss)
                        {
                            x.Pins.Add(serializer.ConvertToType<MapPoint>(item));
                        }

                        //x.Pins = (List<MapPoint>)dictionary["Pins"];
                        return x;

                    }; 
                case "image":
                    {
                        ImageWidget x = new ImageWidget();
                        x.Title = (string)dictionary["Title"];
                        x.ImageURL = (string)dictionary["ImageURL"];
                        x.ThumbnailURL = (string)dictionary["ThumbnailURL"];
                        x.PageURL = (string)dictionary["PageURL"];
                        return x;
                    }; 
                case "html":
                    {
                        HTMLWidget x = new HTMLWidget();
                        x.Title = (string)dictionary["Title"];
                        x.HTML = (string)dictionary["HTML"];
                        return x;


                    }; 
                case "entity":
                    {
                        EntityWidget x = new EntityWidget();
                        x.SubType = (string)dictionary["SubType"];
                        x.Title = (string)dictionary["Title"];
                        x.Abstract = (string)dictionary["Abstract"];
                        x.ImageURL = (string)dictionary["ImageURL"];
                        x.Url = (string)dictionary["Url"];
                        return x;

                    }; 
                case "chart":
                    {
                        ChartWidget x = new ChartWidget();
                        x.Title = (string)dictionary["Title"];
                        //x.Categories = (List<string>)dictionary["Categories"];
                        System.Collections.ArrayList itemss = ((System.Collections.ArrayList)dictionary["Categories"]);
                        foreach (var item in itemss)
                        {
                            x.Categories.Add(serializer.ConvertToType<string>(item));
                        }



                        System.Collections.ArrayList itemss2 = ((System.Collections.ArrayList)dictionary["Data"]);
                        foreach (var item in itemss2)
                        {
                            x.Data.Add(serializer.ConvertToType<ChartsData>(item));
                        }

                        //x.Data = (List<ChartsData>)dictionary["Data"];
                        return x;
                    }; 
                case "businessEntity":
                    {
                        BusinessEntityWidget x = new BusinessEntityWidget();
                        x.SubType = (string)dictionary["SubType"];
                        x.Title = (string)dictionary["Title"];
                        x.Abstract = (string)dictionary["Abstract"];
                        x.ImageURL = (string)dictionary["ImageURL"];
                        x.URL = (string)dictionary["URL"];
                        //x.Attributes = (List<KeyValueTextWidget>)dictionary["Attributes"];
                        System.Collections.ArrayList itemss = ((System.Collections.ArrayList)dictionary["Attributes"]);
                        foreach (var item in itemss)
                        {
                            x.Attributes.Add(serializer.ConvertToType<KeyValueTextWidget>(item));
                        }

                        x.Address = (string)dictionary["Address"];
                        x.Phone = (string)dictionary["Phone"];
                        x.Lat = (double)dictionary["Lat"];
                        x.Lng = (double)dictionary["Lng"];


                        System.Collections.ArrayList itemss2 = ((System.Collections.ArrayList)dictionary["OtherURLs"]);
                        foreach (var item in itemss2)
                        {
                            x.OtherURLs.Add(serializer.ConvertToType<URLWidget>(item));
                        }
                        //x.OtherURLs = (List<URLWidget>)dictionary["OtherURLs"];

                        return x;



                    }; 

                case "list":
                    {
                        switch ((string)dictionary["SubType"])
                        {
                            case null:
                                {
                                    ListWidget x = new ListWidget();
                                    x.Title = (string)dictionary["Title"];
                                    System.Collections.ArrayList itemss = ((System.Collections.ArrayList)dictionary["Items"]);
                                    foreach (var item in itemss)
                                    {
                                        x.Items.Add(serializer.ConvertToType<APIResultWidget>(item));
                                    }
                                    return x;

                                }; 
                            case "videos":
                                {
                                    ListOfVideosWidget x = new ListOfVideosWidget();

                                    System.Collections.ArrayList itemss = ((System.Collections.ArrayList)dictionary["Items"]);
                                    foreach (var item in itemss)
                                    {
                                        x.Items.Add(serializer.ConvertToType<URLWidget>(item));
                                    }
                                    return x;
                                }; 
                            case "images":
                                {
                                    ListOfImagesWidget x = new ListOfImagesWidget();


                                    System.Collections.ArrayList itemss = ((System.Collections.ArrayList)dictionary["Items"]);
                                    foreach (var item in itemss)
                                    {
                                        x.Items.Add(serializer.ConvertToType<ImageWidget>(item));
                                    }
                                    return x;
                                }; 
                            case "webResults":
                                {

                                    ListOfWebsitesWidget x = new ListOfWebsitesWidget();

                                    System.Collections.ArrayList itemss = ((System.Collections.ArrayList)dictionary["Items"]);
                                    foreach (var item in itemss)
                                    {
                                        x.Items.Add(serializer.ConvertToType<URLWidget>(item));
                                    }
                                    return x;
                                }; 
                            case "businesses":
                                {
                                    ListOfBusinessesWidget x = new ListOfBusinessesWidget();
                                    x.Title = (string)dictionary["Title"];
                                    System.Collections.ArrayList itemss = ((System.Collections.ArrayList)dictionary["Items"]);
                                    foreach (var item in itemss)
                                    {
                                        x.Items.Add(serializer.ConvertToType<BusinessEntityWidget>(item));
                                    }
                                    return x;
                                }; 



                        }
                    }; break;


            }
        }

        else //in case of objects not inheriting from the abstract class, in this case we identify each one by something else, not "type"
        {
            if (dictionary.ContainsKey("Day")) //WeatherForcastItem
            {
                WeatherForcastItem x = new WeatherForcastItem();
                x.Day = (string)dictionary["Day"];
                x.Hi = (string)dictionary["Hi"];
                x.Lo = (string)dictionary["Lo"];
                x.Status = (string)dictionary["Status"];
                x.IconURL = (string)dictionary["IconURL"];
                return x;

            }
            else if (dictionary.ContainsKey("Temprature")) // CurrentWeather
            {
                CurrentWeather x = new CurrentWeather();
                x.Temprature = (string)dictionary["Temprature"];
                x.Status = (string)dictionary["Status"];
                x.WindSpeed = (string)dictionary["WindSpeed"];
                x.WindDirection = (string)dictionary["WindDirection"];
                x.Humidity = (string)dictionary["Humidity"];
                x.IconURL = (string)dictionary["IconURL"];
                x.IsNight = (string)dictionary["IsNight"];
                return x;

            }
            else if (dictionary.ContainsKey("Lat")) //MapPoint
            {
                MapPoint x = new MapPoint();
                x.Title = (string)dictionary["Title"];
                x.Lat = (double)dictionary["Lat"];
                x.Lng = (double)dictionary["Lng"];
                return x;
            }
            else if (dictionary.ContainsKey("Value")) //ValueItem
            {
                ValueItem x = new ValueItem();
                x.Value = (string)dictionary["Value"];
                x.ValueURL = (string)dictionary["ValueURL"];
                return x;
            }
            else if (dictionary.ContainsKey("name")) //ChartsData
            {
                ChartsData x = new ChartsData();
                x.name = (string)dictionary["name"];
                System.Collections.ArrayList itemss = ((System.Collections.ArrayList)dictionary["name"]);
                foreach (var item in itemss)
                {
                    x.values.Add(serializer.ConvertToType<string>(item));
                }
                return x;
            }
        }
        return null;
    }

    public override IDictionary<string, object> Serialize(
 object obj,
 JavaScriptSerializer serializer)
    { return null; }

    private static readonly Type[] _supportedTypes = new[]
{
    typeof( APIResultWidget )
};

    public override IEnumerable<Type> SupportedTypes
    {
        get { return _supportedTypes; }
    }

}

各jsonオブジェクトを適切なクラスにマップする必要があるため、デシリアライザーの使用はかなり簡単です。

var serializer = new JavaScriptSerializer();
serializer.RegisterConverters(new[] { new myCustomResolver() });
var dataObj = serializer.Deserialize<SearchAPIResult>(response);

これにより、抽象クラスで発生していた逆シリアル化の問題が解決されました。これは、そもそもクラスが互いにどのように関連しているかを完全に回避したためです。これが最も正しい解決策かどうかはわかりませんが、少なくとも問題は解決しました

于 2013-05-15T09:10:37.970 に答える
0

まず第一に、これはオブジェクトをデシリアライズするためのより良い方法です:

var converted = JsonConvert.DeserializeObject<SearchAPIResult>(json);

とにかく、あなたの問題は、シリアライザーに APIResultWidget のリストのみを使用して Item プロパティを逆シリアル化するように指示しようとしていることだと思います。そのクラスで投稿を更新できますか? 私たちがあなたを助けるのは簡単かもしれません。

于 2013-05-13T14:30:09.043 に答える