JSON.NETを使用したJSONデータのC#への逆シリアル化


144

私はC#とJSONデータの操作に比較的慣れていないため、ガイダンスを求めています。私はC#3.0と.NET3.5SP1、およびJSON.NET 3.5r6を使用しています。

JSON構造から移植する必要がある定義済みのC#クラスがあります。ただし、Webサービスから取得されるエントリのすべてのJSON構造に、C#クラス内で定義されているすべての可能な属性が含まれているわけではありません。

私は間違っていると思われることを困難な方法で行っており、JObjectから各値を1つずつ取り出して、文字列を目的のクラスプロパティに変換しています。

JsonSerializer serializer = new JsonSerializer();
var o = (JObject)serializer.Deserialize(myjsondata);

MyAccount.EmployeeID = (string)o["employeeid"][0];

JSON構造をC#クラスに逆シリアル化し、JSONソースから欠落している可能性のあるデータを処理する最良の方法は何ですか?

私のクラスは次のように定義されています:

  public class MyAccount
  {

    [JsonProperty(PropertyName = "username")]
    public string UserID { get; set; }

    [JsonProperty(PropertyName = "givenname")]
    public string GivenName { get; set; }

    [JsonProperty(PropertyName = "sn")]
    public string Surname { get; set; }

    [JsonProperty(PropertyName = "passwordexpired")]
    public DateTime PasswordExpire { get; set; }

    [JsonProperty(PropertyName = "primaryaffiliation")]
    public string PrimaryAffiliation { get; set; }

    [JsonProperty(PropertyName = "affiliation")]
    public string[] Affiliation { get; set; }

    [JsonProperty(PropertyName = "affiliationstatus")]
    public string AffiliationStatus { get; set; }

    [JsonProperty(PropertyName = "affiliationmodifytimestamp")]
    public DateTime AffiliationLastModified { get; set; }

    [JsonProperty(PropertyName = "employeeid")]
    public string EmployeeID { get; set; }

    [JsonProperty(PropertyName = "accountstatus")]
    public string AccountStatus { get; set; }

    [JsonProperty(PropertyName = "accountstatusexpiration")]
    public DateTime AccountStatusExpiration { get; set; }

    [JsonProperty(PropertyName = "accountstatusexpmaxdate")]
    public DateTime AccountStatusExpirationMaxDate { get; set; }

    [JsonProperty(PropertyName = "accountstatusmodifytimestamp")]
    public DateTime AccountStatusModified { get; set; }

    [JsonProperty(PropertyName = "accountstatusexpnotice")]
    public string AccountStatusExpNotice { get; set; }

    [JsonProperty(PropertyName = "accountstatusmodifiedby")]
    public Dictionary<DateTime, string> AccountStatusModifiedBy { get; set; }

    [JsonProperty(PropertyName = "entrycreatedate")]
    public DateTime EntryCreatedate { get; set; }

    [JsonProperty(PropertyName = "entrydeactivationdate")]
    public DateTime EntryDeactivationDate { get; set; }

  }

解析するJSONのサンプルは次のとおりです。

{
    "givenname": [
        "Robert"
    ],
    "passwordexpired": "20091031041550Z",
    "accountstatus": [
        "active"
    ],
    "accountstatusexpiration": [
        "20100612000000Z"
    ],
    "accountstatusexpmaxdate": [
        "20110410000000Z"
    ],
    "accountstatusmodifiedby": {
        "20100214173242Z": "tdecker",
        "20100304003242Z": "jsmith",
        "20100324103242Z": "jsmith",
        "20100325000005Z": "rjones",
        "20100326210634Z": "jsmith",
        "20100326211130Z": "jsmith"
    },
    "accountstatusmodifytimestamp": [
        "20100312001213Z"
    ],
    "affiliation": [
        "Employee",
        "Contractor",
        "Staff"
    ],
    "affiliationmodifytimestamp": [
        "20100312001213Z"
    ],
    "affiliationstatus": [
        "detached"
    ],
    "entrycreatedate": [
        "20000922072747Z"
    ],
    "username": [
        "rjohnson"
    ],
    "primaryaffiliation": [
        "Staff"
    ],
    "employeeid": [
        "999777666"
    ],
    "sn": [
        "Johnson"
    ]
}

回答:



76

一般的なDeserializeObjectメソッドを使用してみましたか?

JsonConvert.DeserializeObject<MyAccount>(myjsondata);

JSONデータで欠落しているフィールドは、単にNULLのままにする必要があります。

更新:

JSON文字列が配列の場合は、次のことを試してください。

var jarray = JsonConvert.DeserializeObject<List<MyAccount>>(myjsondata);

jarrayその後、である必要がありList<MyAccount>ます。

別の更新:

あなたが得ている例外はオブジェクトの配列と一貫していません-私はシリアライザがあなたの辞書型のaccountstatusmodifiedbyプロパティに問題を抱えていると思います。

accountstatusmodifiedby プロパティをシリアル化から除外して、それが役立つかどうかを確認してください。その場合は、そのプロパティを別の方法で表す必要がある場合があります。

ドキュメント:Json.NETを使用したJSONのシリアライズとデシリアライズ


ありがとう。しかし、「JSON配列を型 'System.String'にデシリアライズできません。」というエラーが表示されます。(たとえば)JSONのgivenname配列をクラスGivenName文字列に逆シリアル化しようとしているとき。C#クラスで文字列として定義したJSON属性は、単一の要素配列のみです。これが、逆シリアル化プロセス中にこの種の問題に遭遇したときに、値を1つずつ選択し始めた理由です。私が見落としている他の魔法?

したがってDateTime AccountStatusExpiration、コードで定義されているように... (たとえば)はnullにできません。それをヌル可能にするためには何が必要ですか?単にに変更DateTimeDateTime?ますか?
Hamish Grubijan、2012

50

https://stackoverflow.com/a/10718128/776476から複製された回答

C#dynamicタイプを使用すると、物事を簡単にすることができます。この手法では、マジックストリングに依存しないため、リファクタリングも簡単になります。

ジェイソン

json以下の文字列はhttp api呼び出しからの単純な応答であり、2つのプロパティを定義しています:IdおよびName

{"Id": 1, "Name": "biofractal"}

C#

JsonConvert.DeserializeObject<dynamic>()この文字列を動的型に逆シリアル化し、通常の方法でそのプロパティにアクセスするために使用します。

var results = JsonConvert.DeserializeObject<dynamic>(json);
var id = results.Id;
var name= results.Name;

:NewtonSoftアセンブリのNuGetリンクはhttp://nuget.org/packages/newtonsoft.jsonです。追加することを忘れないでください:using Newtonsoft.Json;それらのクラスにアクセスします。


7
<dynamic>の使用が大好きです。ただし、これを機能させるには、result ["Id"]。Valueおよびresult ["Name"]。Value
fredw

1
動的な方法は良い代替手段ですが、上記の例では「マジックストリング」を使用していませんが、通常のVSリファクタリング手法中に更新される強く型付けされたジェネリックを使用しています(この質問の範囲外のMVCのビュー内を除く)。
gcoleman0828 2014年

4
まだ「マジックストリング」が残っていますが、ダイナミックを使用することで非表示になります。
Ian Ringrose

確かに、@ IanRingroseが指摘しているように、バイオフラクタルは「魔法のひも」を逆にしたものです。によって返される動的オブジェクトDeserializeObject<dynamic>は、定義上「タイプチェック」されていません。次の行はそうresults.Idと、results.Nameコンパイル時に検証することができません。代わりに、実行時にエラーが発生します。これを、などの強く型付けされた呼び出しと比較してくださいDeserializeObject<List<MyAccount>>。これらのプロパティへの参照は、コンパイル時に確認できます。の使用はdynamic便利かもしれませんが、プロパティ名として「マジックストリング」を導入します。堅牢性の低下IMHO。
ToolmakerSteve、

8

以下を使用できます。

JsonConvert.PopulateObject(json, obj);

ここで:jsonjson文字列、objはターゲットオブジェクトです。参照:

注:PopulateObject()objのリストデータは消去されません。その後Populate()obj'sリストメンバーには元のデータとjson文字列のデータが含まれます


2
PopulateObject-Populateはオブジェクトモデルにありません。
amok 2012年

1
これは私にとって完璧に働きました!かなり複雑なJSON文字列がある問題がありました。C#オブジェクトにキャストしようとすると、「NotNull」とマークされたものは、最初からJSON文字列に存在していたとしても、オブジェクトから失われていました。非常に奇妙な。私はこの方法を使用し、それは完璧に機能しました!
jward01 2016年

3

bbantの答えを基にして、これはリモートURLからJSONをデシリアライズするための完全なソリューションです。

using Newtonsoft.Json;
using System.Net.Http;

namespace Base
{
    public class ApiConsumer<T>
    {
        public T data;
        private string url;

        public CalendarApiConsumer(string url)
        {
            this.url = url;
            this.data = getItems();
        }

        private T getItems()
        {
            T result = default(T);
            HttpClient client = new HttpClient();

            // This allows for debugging possible JSON issues
            var settings = new JsonSerializerSettings
            {
                Error = (sender, args) =>
                {
                    if (System.Diagnostics.Debugger.IsAttached)
                    {
                        System.Diagnostics.Debugger.Break();
                    }
                }
            };

            using (HttpResponseMessage response = client.GetAsync(this.url).Result)
            {
                if (response.IsSuccessStatusCode)
                {
                    result = JsonConvert.DeserializeObject<T>(response.Content.ReadAsStringAsync().Result, settings);
                }
            }
            return result;
        }
    }
}

使い方は次のようになります:

ApiConsumer<FeedResult> feed = new ApiConsumer<FeedResult>("http://example.info/feeds/feeds.aspx?alt=json-in-script");

Xamasoft JSON Class GeneratorFeedResultを使用して生成されたクラスはどこにありますか

これは私が使用した設定のスクリーンショットです。これは、Webバージョンでは説明できない奇妙なプロパティ名を許可しています。

Xamasoft JSONクラスジェネレーター


1

私は自分のオブジェクトを誤って構築したことがわかりました。私はhttp://json2csharp.com/を使用して、JSONからオブジェクトクラスを生成しました。正しいOjectを入手したら、問題なくキャストすることができました。Norbit、Noobの間違い。同じ問題が発生する場合に備えて追加します。


1

詳細については、いくつかのクラスジェネレーターをオンラインで確認してみてください。ただし、いくつかの回答は役に立ちました。役に立つかもしれない私のアプローチはここにあります。

次のコードは、動的メソッドを念頭に置いて作成されました。

dynObj = (JArray) JsonConvert.DeserializeObject(nvm);

foreach(JObject item in dynObj) {
 foreach(JObject trend in item["trends"]) {
  Console.WriteLine("{0}-{1}-{2}", trend["query"], trend["name"], trend["url"]);
 }
}

このコードでは、基本的に、Json文字列に含まれているメンバーにアクセスできます。クラスを必要としない、まったく異なる方法。querytrendおよびurlJson文字列に含まれるオブジェクトです。

こちらのウェブサイトもご利用いただけます。クラスを100%信用しないでください。


0

サンプルデータが正しいと仮定すると、与えられた名前、および角かっこで囲まれた他のエントリはJSの配列です...これらのデータ型にはListを使用します。そして、たとえばaccountstatusexpmaxdateのリスト...日付の形式が間違っていると思いますので、他に何が間違っているのか不明です。

これは古い投稿ですが、問題を書き留めておきたかったのです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.