🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
.NET Core

.NET Coreは、マネージソフトウェアフレームワークでオープンソースで実装されています。クロスプラットフォームを前提に考えられており、Windows/Mac/Linuxで動くアプリケーションを作成することが可能です。

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

JSON

JSON(JavaScript Object Notation)は軽量なデータ記述言語の1つである。構文はJavaScriptをベースとしていますが、JavaScriptに限定されたものではなく、様々なソフトウェアやプログラミング言語間におけるデータの受け渡しが行えるように設計されています。

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

配列

配列は、各データの要素(値または変数)が連続的に並べられたデータ構造です。各配列は添え字(INDEX)で識別されています。

Q&A

解決済

3回答

18260閲覧

【C#】JSONのデシリアライズで、配列の場合とそうでない場合の際の対処法が知りたい

Leizi

総合スコア8

.NET Core

.NET Coreは、マネージソフトウェアフレームワークでオープンソースで実装されています。クロスプラットフォームを前提に考えられており、Windows/Mac/Linuxで動くアプリケーションを作成することが可能です。

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

JSON

JSON(JavaScript Object Notation)は軽量なデータ記述言語の1つである。構文はJavaScriptをベースとしていますが、JavaScriptに限定されたものではなく、様々なソフトウェアやプログラミング言語間におけるデータの受け渡しが行えるように設計されています。

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

配列

配列は、各データの要素(値または変数)が連続的に並べられたデータ構造です。各配列は添え字(INDEX)で識別されています。

1グッド

0クリップ

投稿2021/02/04 01:35

編集2021/02/04 03:56

前提・実現したいこと

下記サイトで、配列とそうではい場合はobject型で受けて別途キャストが必要と記載されていましたが(下記画像)、
これは値を使用する際に毎回キャストが必要なんでしょうか?
object型になっている部分そのものをキャストして持てるんでしょうか?(クラス自体のキャスト)
public object api_get_meateriak → public List<int> api_get_meateriak

うまく伝わっているかわかりませんが、デシアライズしたクラスにキャスト後の値を持てるのかということです。
クラスを使う際に
「ApiData.api_get_meateriak(object)」ではなく「ApiData.api_get_meateriak(List<int>)」として使えるのか

宜しくお願いします。

http://www2.hatenadiary.jp/entry/2013/12/14/030112

イメージ説明

追記1

JSON 文字列のサンプルを書いて、それのどこで悩んでいるのか具体的に書いてもらえませんか? 今の質問内容ではそのあたりが分からないです。

jsonを送られてくる可能性として、配列の場合とそうでない場合があります

json

1{ 2 "LoginList": { 3 "LoginItem": [ 4 { 5 "ID": "TEST01" 6 }, 7 { 8 "ID": "TEST02" 9 } 10 ] 11 } 12}

json

1{ 2 "LoginList": { 3 "LoginItem": { 4 "ID": "TEST01" 5 } 6 } 7}

visual studioの機能でjsonからクラスを作成

C#

1 public class Rootobject 2 { 3 public Loginlist LoginList { get; set; } 4 } 5 6 public class Loginlist 7 { 8 public Loginitem LoginItem { get; set; } 9 } 10 11 public class Loginitem 12 { 13 public string ID { get; set; } 14 }

しかしこのままだと配列ではない場合はデシリアライズが成功しますが、配列だとExceptionになります
逆にpublic List<Loginitem> LoginItem { get; set; }にすると配列でない場合にExceptionになります

なのでそれを避けるために上記で記載したとおりpublic object LoginItem { get; set; }にするという記事を見つけました
ですが、obejctだとそのまま使用できないのでキャストが必要となるらしいのですが、
イメージ説明

配列ではないのならば下記のように持ち
イメージ説明

配列なら下記のように持つことが出来ないのか
イメージ説明

上記のようにクラス自体はどうにもならないのであれば、LoginItem の中身を使用する際に都度キャストが必要なのかという内容です。

補足情報(FW/ツールのバージョンなど)

Visual Studio2019
.Net Core 3.1
C#
Newtonsoft.Json

TN8001👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2021/02/04 03:25

.Net Core 3.1 で Newtonsoft.Json というのは間違いないですか? System.Text.Json というのがあるのに知らずに使っているということはないですか? JSON 文字列のサンプルを書いて、それのどこで悩んでいるのか具体的に書いてもらえませんか? 今の質問内容ではそのあたりが分からないです。
Leizi

2021/02/04 03:57

SurferOnWwwさん 回答有り難うございます。 > System.Text.Json というのがあるのに知らずに使っているということはないですか? あるのは知っていますが、Newtonsoft.Jsonを使用しています >JSON 文字列のサンプルを書いて、それのどこで悩んでいるのか具体的に書いてもらえませんか? 追記致しました、確認お願いします
退会済みユーザー

退会済みユーザー

2021/02/04 04:02

JSON 文字列が不定で決まった T の定義ができないということなら、それは配列か否か云々を超えて悩まなければならない話と思いますが、そういう場合でも Dictionary<string,object> 型にならデシリアライズできるので、それからどうするか考えるという話になるかと思います。
Leizi

2021/02/04 04:10

var model = JsonConvert.DeserializeObject<Dictionary<string, object>>(request); にするということでしょうか? これはこれで難しいんですが…
退会済みユーザー

退会済みユーザー

2021/02/04 05:05

> これはこれで難しいんですが… 難しい云々以前に、JSON 文字列が不定で決まった T の定義ができないということならそうせざるを得ないかと思います。 質問者さんが気にしている配列か否かで悩むという単純なケースはまず無さそうな気がします。配列になることがあるなら設計時点で配列にして、API 仕様に明記しておけばすむ話だと思います。要素数が 1 つの配列だってあるわけですから。取得するまでどっちか分からないからユーザーによしなに対応してくれというのでは設計がまずいです。
Leizi

2021/02/04 05:13 編集

jsonがほとんど初めて触るので初歩的な質問になってしまうかもしれませんが、要素が1つの配列はjsonの形式として有りなんでしょうか? 例えばxmlから変換すると配列じゃなくなってしまうのですが <LoginList> <LoginItem> <ID>TEST01</ID> </LoginItem> </LoginList> ↓ { "LoginList": { "LoginItem": { "ID": "TEST01" } } } 、それは変換の仕様であって普通に { "LoginList": { "LoginItem": [ { "ID": "TEST01" } ] } } として作成できるのでしょうか? ここのコメントですいません…
退会済みユーザー

退会済みユーザー

2021/02/04 05:42

> 要素が1つの配列はjsonの形式として有りなんでしょうか? 有りです。 > 例えばxmlから変換すると配列じゃなくなってしまうのですが XML から変換って何なのですか? このスレッドでは初めて出てきた話のようですが?
Leizi

2021/02/04 06:03

例えで出したので、特に気にしなくても問題有りません。 ありがとうございました。
guest

回答3

0

ベストアンサー

型が変わってしまったら、ただただ扱いづらいだけです。
実際にやりたいことは、どちらを読んでもList<Loginitem>になることではないですか?

sendgrid - How to handle both a single item and an array for the same property using JSON.net - Stack Overflow

cs

1using Newtonsoft.Json; 2using Newtonsoft.Json.Linq; 3using System; 4using System.Linq; 5using System.Collections.Generic; 6 7namespace Questions320367 8{ 9 class Program 10 { 11 static void Main() 12 { 13 var arrayJson = @" 14{ 15 ""LoginList"": { 16 ""LoginItem"": [ 17 { 18 ""ID"": ""TEST01"" 19 }, 20 { 21 ""ID"": ""TEST02"" 22 } 23 ] 24 } 25}"; 26 var result = JsonConvert.DeserializeObject<Rootobject>(arrayJson); 27 List<Loginitem> loginItem = result.LoginList.LoginItem; 28 29 Console.WriteLine(loginItem.Count); 30 Console.WriteLine(string.Join(", ", loginItem.Select(x => x.ID))); 31 32 33 var singleJson = @" 34{ 35 ""LoginList"": { 36 ""LoginItem"": { 37 ""ID"": ""TEST01"" 38 } 39 } 40}"; 41 result = JsonConvert.DeserializeObject<Rootobject>(singleJson); 42 loginItem = result.LoginList.LoginItem; 43 44 Console.WriteLine(loginItem.Count); 45 Console.WriteLine(string.Join(", ", loginItem.Select(x => x.ID))); 46 } 47 } 48 49 public class Rootobject 50 { 51 public Loginlist LoginList { get; set; } 52 } 53 public class Loginlist 54 { 55 [JsonConverter(typeof(SingleOrArrayConverter<Loginitem>))] 56 public List<Loginitem> LoginItem { get; set; } 57 } 58 public class Loginitem 59 { 60 public string ID { get; set; } 61 } 62 63 // [sendgrid - How to handle both a single item and an array for the same property using JSON.net - Stack Overflow](https://stackoverflow.com/questions/18994685/how-to-handle-both-a-single-item-and-an-array-for-the-same-property-using-json-n/18997172#18997172) 64 internal class SingleOrArrayConverter<T> : JsonConverter 65 { 66 public override bool CanConvert(Type objectType) => objectType == typeof(List<T>); 67 public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 68 { 69 var token = JToken.Load(reader); 70 if (token.Type == JTokenType.Array) 71 { 72 return token.ToObject<List<T>>(); 73 } 74 return new List<T> { token.ToObject<T>() }; 75 } 76 77 public override bool CanWrite => false; 78 public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) => throw new NotImplementedException(); 79 } 80}

System.Text.Json版

c# - How to handle both a single item and an array for the same property using System.Text.Json? - Stack Overflow

cs

1using System; 2using System.Collections; 3using System.Collections.Generic; 4using System.Linq; 5using System.Text.Json; 6using System.Text.Json.Serialization; 7 8namespace Questions320367 9{ 10 class Program 11 { 12 static void Main() 13 { 14 var arrayJson = @" 15{ 16 ""LoginList"": { 17 ""LoginItem"": [ 18 { 19 ""ID"": ""TEST01"" 20 }, 21 { 22 ""ID"": ""TEST02"" 23 } 24 ] 25 } 26}"; 27 var result = JsonSerializer.Deserialize<Rootobject>(arrayJson); 28 List<Loginitem> loginItem = result.LoginList.LoginItem; 29 30 Console.WriteLine(loginItem.Count); 31 Console.WriteLine(string.Join(", ", loginItem.Select(x => x.ID))); 32 33 34 var singleJson = @" 35{ 36 ""LoginList"": { 37 ""LoginItem"": { 38 ""ID"": ""TEST01"" 39 } 40 } 41}"; 42 result = JsonSerializer.Deserialize<Rootobject>(singleJson); 43 loginItem = result.LoginList.LoginItem; 44 45 Console.WriteLine(loginItem.Count); 46 Console.WriteLine(string.Join(", ", loginItem.Select(x => x.ID))); 47 } 48 } 49 50 public class Rootobject 51 { 52 public Loginlist LoginList { get; set; } 53 } 54 public class Loginlist 55 { 56 [JsonConverter(typeof(SingleOrArrayConverter<Loginitem>))] 57 public List<Loginitem> LoginItem { get; set; } 58 } 59 public class Loginitem 60 { 61 public string ID { get; set; } 62 } 63 64 65 // [c# - How to handle both a single item and an array for the same property using System.Text.Json? - Stack Overflow](https://stackoverflow.com/questions/59430728/how-to-handle-both-a-single-item-and-an-array-for-the-same-property-using-system) 66 public class SingleOrArrayConverter<TItem> : SingleOrArrayConverter<List<TItem>, TItem> 67 { 68 public SingleOrArrayConverter() : this(true) { } 69 public SingleOrArrayConverter(bool canWrite) : base(canWrite) { } 70 } 71 72 public class SingleOrArrayConverter<TCollection, TItem> : JsonConverter<TCollection> where TCollection : class, ICollection<TItem>, new() 73 { 74 public bool CanWrite { get; } 75 76 public SingleOrArrayConverter() : this(true) { } 77 public SingleOrArrayConverter(bool canWrite) => CanWrite = canWrite; 78 79 public override TCollection Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) 80 { 81 switch (reader.TokenType) 82 { 83 case JsonTokenType.Null: return null; 84 case JsonTokenType.StartArray: 85 var list = new TCollection(); 86 while (reader.Read()) 87 { 88 if (reader.TokenType == JsonTokenType.EndArray) break; 89 list.Add(JsonSerializer.Deserialize<TItem>(ref reader, options)); 90 } 91 return list; 92 default: 93 return new TCollection { JsonSerializer.Deserialize<TItem>(ref reader, options) }; 94 } 95 } 96 public override void Write(Utf8JsonWriter writer, TCollection value, JsonSerializerOptions options) 97 { 98 if (CanWrite && value.Count == 1) 99 { 100 JsonSerializer.Serialize(writer, value.First(), options); 101 } 102 else 103 { 104 writer.WriteStartArray(); 105 foreach (var item in value) JsonSerializer.Serialize(writer, item, options); 106 writer.WriteEndArray(); 107 } 108 } 109 } 110}

投稿2021/02/04 09:26

編集2023/08/13 12:45
TN8001

総合スコア9855

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Leizi

2021/02/05 05:54

TN8001さん 回答ありがとうございます。 自分で思い描いている感じとして理想形な解決法でした!
guest

0

質問の私のコメントに対し追記1を書いていただけたので、それに対するレスを以下に書きます。

上記のようにクラス自体はどうにもならないのであれば、LoginItem の中身を使用する際に都度キャストが必要なのかという内容です。

JSON 文字列が、

{"LoginList":{"LoginItem":[{"ID":"TEST01"},{"ID":"TEST02"}]}}

でも、

{"LoginList":{"LoginItem":{"ID":"TEST01"}}}

でも、

var rootObject = JsonConvert.DeserializeObject<Rootobject>(JSON 文字列)

でデシリアライズできるようにするため、Rootobject の定義を、

public class Rootobject { public Loginlist LoginList { get; set; } } public class Loginlist { public object LoginItem { get; set; } } public class Loginitem { public string ID { get; set; } }

のようにしたが、その先どのように Loginitem を取得するかという話と理解しています。理解が違っていたら指摘してください。

上記の Rootobject クラスの定義を使ってデシリアライズした結果の rootObject.LoginList.LoginItem は Newtonsoft.Json.Linq.JArray 型または Newtonsoft.Json.Linq.JObject 型になります。

なので、単純に Loginitem または Loginitem[] にキャストして何とかできるということはなくて、JArray, JObject にキャストしてそのプロパティなどを使って Value を取得することになります。

具体例は以下の通りです。

Console.WriteLine("------Newtonsoft.Json---------------------"); string str1 = "{\"LoginList\":{\"LoginItem\":[{\"ID\":\"TEST01\"},{\"ID\":\"TEST02\"}]}}"; var ro1 = JsonConvert.DeserializeObject<Rootobject>(str1); var items1 = ro1.LoginList.LoginItem as JArray; if (items1 != null) { foreach (JObject jobj in items1) { JValue idValue = (JValue)jobj["ID"]; string value = (string)idValue.Value; Console.WriteLine($"ID: {value}"); } } Console.WriteLine("---------------------------"); string str2 = "{\"LoginList\":{\"LoginItem\":{\"ID\":\"TEST01\"}}}"; var ro2 = JsonConvert.DeserializeObject<Rootobject>(str2); var item2 = ro2.LoginList.LoginItem as JObject; if (item2 != null) { JValue idValue = (JValue)item2["ID"]; string value = (string)idValue.Value; Console.WriteLine($"ID: {value}"); }

結果は以下のようになります。

------Newtonsoft.Json--------------------- ID: TEST01 ID: TEST02 --------------------------- ID: TEST01

投稿2021/02/04 09:13

編集2021/02/04 21:32
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Leizi

2021/02/05 05:57

SurferOnWwwさん 回答ありがとうございます。 object型の対応としては、理想的だと思いました。
guest

0

悩みのポイントがよくわかんないけど、ApiData.api_get_meateriakJArray型か否かを判別する方法までわかっているなら、それがtrueの時にToObject<T>()メソッドを使ってその値をデシリアライズした値を持っておけばいいだけじゃないだろうか。

投稿2021/02/04 02:14

gentaro

総合スコア8947

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Leizi

2021/02/04 02:22

gentaroさん 回答有り難うございます。 結局の所、クラス自体はobject型のままで、その値(object型の中身)を使用したい場合は別途デシリアライズした値を使うしか無いってことで宜しいでしょうか?(元のクラスとしては使えない) 自分も伝え方が難しいです…
gentaro

2021/02/04 12:02 編集

そもそも論としてC#は静的型付けの言語なので、配列の場合はList<T>にマップする、そうでない場合はTにマップする、といったように、マップするクラスまたはプロパティをそれぞれ用意しないと、たぶんあなたの質問にあるような「毎回キャストが必要」な状況は避けられません。 それをデシリアライズ時にやるのか、object等にいったん入れておいて利用する際にやるのか、という違いでしか無いです。 一応dynamicという静的型付けのメリットを捨て去る方法もあるけど、コンパイル時エラーが補足できなくなるなどのデメリットもあるので、あんまりお勧めはしません。
Leizi

2021/02/05 05:58

理解しました。 ありがとうございます!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問