JSON.NET から C# オブジェクトへ
質問
Windows フォームで JSON.NET フレームワークを使用して、JSON 文字列から情報を読み取ろうとしています。しかし、「分類->トピック」配列と「クラスター」から辞書を取得するのに苦労しています
{
"keywords": {
"anyString": [
],
"allString": {
"a5349f533e3aa3ccbc27de2638da38d6": "olympics"
},
"exactString": [
],
"notString": [
],
"highlightString": [
]
},
"dates": {
"startDate": "15-01-2008",
"endDate": "15-09-2009",
"useDates": true
},
"clusters": {
"permission": {
"1": "private\/n"
}
},
"taxonomies": {
"Topics": {
"2488": "Olympics 2012 (not participation)",
"8876": "Olympics and culture"
},
"Keywords": {
"8848": "Engineering in the Olympics"
}
},
"sort": {
"sortId": 1,
"sortType": 2,
"sort": "datetime",
"sortOrder": "descending"
}
}
以下のコードを使用すると、情報の一部を読み取ることができました。
JObject searchCriteria = JObject.Parse(contentSearchCriteria);
//search criteria
IEnumerable<string> allString = searchCriteria["keywords"]["allString"].Children().Values<string>();
IEnumerable<string> anyString = searchCriteria["keywords"]["anyString"].Children().Values<string>();
IEnumerable<string> notString = searchCriteria["keywords"]["notString"].Children().Values<string>();
IEnumerable<string> exactString = searchCriteria["keywords"]["exactString"].Children().Values<string>();
IEnumerable<string> highlightString = searchCriteria["keywords"]["highlightString"].Children().Values<string>();
//dates
string startDate = (string)searchCriteria["dates"]["startDate"];
string endDate = (string)searchCriteria["dates"]["endDate"];
bool useDates = (bool)searchCriteria["dates"]["useDates"];
//sort
int sortId = (int)searchCriteria["sort"]["sortId"];
int sortType = (int)searchCriteria["sort"]["sortType"];
string sort = (string)searchCriteria["sort"]["sort"];
string sortOrder = (string)searchCriteria["sort"]["sortOrder"];
アップデート:
推奨どおり追加しました
class SMSearchCriteria
{
public SMKeywords keywords { get; set; }
public SMDates dates { get; set; }
public SMClusters clusters { get; set; }
public SMTaxonomies taxonomies { get; set; }
public SMSort sort { get; set; }
}
class SMKeywords
{
public List<Dictionary<string, string>> AnyString {get; set;}
public List<Dictionary<string, string>> AllString { get; set; }
public List<Dictionary<string, string>> ExactString { get; set; }
public List<Dictionary<string, string>> NotString { get; set; }
public List<Dictionary<string, string>> HighlightString { get; set; }
}
class SMDates
{
public string startDate { get; set; }
public string endDate { get; set; }
public bool useDates { get; set; }
}
class SMClusters
{
List<SMCluster> cluster;
}
class SMCluster
{
public Dictionary<string, string> cluster { get; set; }
}
class SMTaxonomies
{
public List<SMTaxonomy> taxonomies { get; set; }
}
class SMTaxonomy
{
public Dictionary<string, List<SMCategory>> taxonomy { get; set; }
}
class SMCategory
{
public Dictionary<int, string> category { get; set; }
}
class SMSort
{
public int sortId { get; set; }
public int sortType { get; set; }
public string sort { get; set; }
public string sortOrder { get; set; }
}
しかし、私が実行すると:
var mydata = JsonConvert.DeserializeObject<SMSearchCriteria>(contentSearchCriteria);
例外が発生します:
[Newtonsoft.Json.JsonSerializationException] = {"Cannot deserialize JSON object into type 'System.Collections.Generic.List`1[System.Collections.Generic.Dictionary`2[System.String,System.String]]'."}
更新 2:
提案されているように、余分なリストをすべて削除し、クラスをこれに単純化しました。
class SearchMasterSearchCriteria
{
public SMKeywords keywords { get; set; }
public SMDates dates { get; set; }
public Dictionary<string, Dictionary<int, string>> clusters { get; set; }
public Dictionary<string, Dictionary<int, string>> taxonomies { get; set; }
public SMSort sort { get; set; }
}
class SMKeywords
{
public Dictionary<string, string> anyString {get; set;}
public Dictionary<string, string> allString { get; set; }
public Dictionary<string, string> exactString { get; set; }
public Dictionary<string, string> notString { get; set; }
public Dictionary<string, string> highlightString { get; set; }
}
class SMDates
{
public string startDate { get; set; }
public string endDate { get; set; }
public bool useDates { get; set; }
}
class SMSort
{
public int sortId { get; set; }
public int sortType { get; set; }
public string sort { get; set; }
public string sortOrder { get; set; }
}
次のようにオブジェクトをシリアル化するためのテスト コードも追加しました。
//criteria
SearchMasterSearchCriteria smCriteria = new SearchMasterSearchCriteria();
//keywords
SMKeywords smKeywords = new SMKeywords(); ;
Dictionary<string, string> dict = new Dictionary<string, string>();
dict.Add("a5349f533e3aa3ccbc27de2638da38d6", "olympics");
dict.Add("9cfa7aefcc61936b70aaec6729329eda", "games");
smKeywords.allString = dict;
//category
Dictionary<int, string> categorieDict = new Dictionary<int, string>();
categorieDict.Add(2488, "Olympics 2012 (not participation)");
categorieDict.Add(8876, "Olympics and culture");
//taxonomies
Dictionary<string, Dictionary<int, string>> taxonomiesDict = new Dictionary<string, Dictionary<int, string>>();
taxonomiesDict.Add("Topics", categorieDict);
//metadata
Dictionary<int, string> metadataDict = new Dictionary<int, string>();
metadataDict.Add(1, @"private/n");
//clusters
Dictionary<string, Dictionary<int, string>> clustersDict = new Dictionary<string, Dictionary<int, string>>();
clustersDict.Add("permission", metadataDict);
//dates
SMDates smDates = new SMDates();
smDates.startDate = "15-01-2008";
smDates.endDate = "15-09-2009";
smDates.useDates = true;
//sort
SMSort smSort = new SMSort();
smSort.sortId = 1;
smSort.sortType = 2;
smSort.sort = "datetime";
smSort.sortOrder = "descending";
//add to criteria.
smCriteria.keywords = smKeywords;
smCriteria.clusters = clustersDict;
smCriteria.taxonomies = taxonomiesDict;
smCriteria.dates = smDates;
smCriteria.sort = smSort;
//serialize
string json = JsonConvert.SerializeObject(smCriteria);
var mydata1 = JsonConvert.DeserializeObject<SearchMasterSearchCriteria>(json);
その時点では、2 つの JSON 文字列の唯一の違いは次のとおりです。[] と、anyString、exactString などの null。そこで、空の角括弧を中括弧に置き換えたところ、エラーなしでデシリアライズされました:)
contentSearchCriteria = contentSearchCriteria.Replace("[]", "{}");
var mydata = JsonConvert.DeserializeObject<SearchMasterSearchCriteria>(contentSearchCriteria);
解決
正直に言うと、私はあなたがやっているようなやり方は絶対にしません。データを取得する方法は次のとおりです。
class Data {
Dictionary<string, Dictionary<string, string>> keywords;
DatesClass dates;
.......
}
class DatesClass
{
string startDate;
string endDate;
bool? useDates
}
var mydata = JsonConvert.DeserializeObject<Data>(jsonstring);
データ クラス全体を入力しませんでしたが、要点は理解できました。入力データの構造内にオブジェクトを作成し、DeserializeObject メソッドを使用してデータを埋める方がはるかに簡単であることがわかりました。これにより、コードがよりクリーンになり、コンパイラーがタイプミスをチェックできるようになります。
他のヒント
はい、今の問題は、JSON.net がオブジェクトを逆シリアル化する方法にあります。JSON.net では、C# クラスは Json オブジェクトになります。そして、そのクラスのメンバーはキーになり、そのメンバーの値が値になります。
例としてタクソノミーパスの例を見てみましょう。上記のクラス定義を使用して、JSON.net は次の形式の Json データを探します。
{"taxonomies": {"taxonomies":[{"taxonomy": {"Topics": {1212, "foo"}}}]}
これは入力データとまったく似ていません。
オブジェクトを作成するときは、次のように考えてください。
1) 基本オブジェクトは、JSON コード内に {} を作成します。2) 辞書は、JSON コード内に {} を作成します。3)リストはJSONコード4に[]を作成します4)クラスのすべてのメンバーは、JSONコードの{}にエントリを作成します
これをデバッグするには、構造を作成し、一時データを入力して、JsonConvert.Serialize(myobj) を使用して、JSON が構造がどのように見えるかを示すと便利です。
あなたの例外は、多くのクラスに参加する方法があることに起因すると思います。
コードの分類部分はおそらく次のようになります。
class SMSearchCriteria
{
public SMKeywords keywords { get; set; }
public SMDates dates { get; set; }
public SMClusters clusters { get; set; }
public SMTaxominies taxonomies { get; set; }
public SMSort sort { get; set; }
}
class SMTaxominies
{
public Dictionary<string, string> Topics;
public Keywords<string, string> Keywords;
}
私は、強く型付けされた dto クラスから始めますが、できれば DataContract を使用して、必要な形式にシリアル化する選択肢を取得します。JSON、Xml、ProtoBuf など
注記:実際、JSON は他のほとんどの形式と比較して、シリアル化/シリアル化解除がかなり遅いです (以下を参照)。 シリアル化ベンチマーク - JSON.NET は「NewtonSoft.Json」です) Web アプリではなくリッチ クライアント アプリを実行している場合は、別のシリアル化形式を選択することをお勧めします。最終的にどの形式になるかに関係なく、同じ DTO を再利用できます。上記のコードは次のようになります。
[DataContract]
public class MyDto
{
[DataMember]
public Keywords keywords { get; set; }
}
[DataContract]
public class Keywords
{
[DataMember]
public List<string> anyString { get; set; }
[DataMember]
public Dictionary<string,string> allString { get; set; }
[DataMember]
public List<string> exactString { get; set; }
[DataMember]
public List<string> notString { get; set; }
[DataMember]
public List<string> highlightString { get; set; }
}
var dto = new MyDto { Keywords = { allString = {{"a5349f533e3aa3ccbc27de2638da38d6", "olympics"}} };
var json = JsonConvert.SerializeObject(dto);
var fromJson = JsonConvert.DeserializeObject<MyDto>(json);
編集: リンクを追加しました
JSON.NET で問題が発生している可能性があるようです。その場合は、他の JSON .NET シリアライザー/デシリアライザーを試してください。Microsoft は、.NET v3.5 に含まれる System.Runtime.Serialization.Json.DataContractJsonSerializer を出荷しています。以下にいくつかのヘルパー クラスを示します。 シリアライズ そして デシリアライズ JSON。
ジェイロック .NET 用のもう 1 つの JSON シリアライザーですが、他のシリアライザーよりも遅く、ジェネリックに対する適切なサポートがありません。
LINQ to Jsonを使ってみませんか?
たとえば、クラスにマップされた「sort」ノードを取得します。
var jsonResult = JObject.Parse(jsonString);
var sortItem = from s in jsonResult["sort"]
select new MySortObject{
SortId = s.Value<int>("sortId")
};