teratail header banner
teratail header banner
質問するログイン新規登録

回答編集履歴

4

見直しキャンペーン中

2023/08/13 12:45

投稿

TN8001
TN8001

スコア10108

answer CHANGED
@@ -163,44 +163,6 @@
163
163
  public SingleOrArrayConverter(bool canWrite) : base(canWrite) { }
164
164
  }
165
165
 
166
- public class SingleOrArrayConverterFactory : JsonConverterFactory
167
- {
168
- public bool CanWrite { get; }
169
-
170
- public SingleOrArrayConverterFactory() : this(true) { }
171
- public SingleOrArrayConverterFactory(bool canWrite) => CanWrite = canWrite;
172
-
173
- public override bool CanConvert(Type typeToConvert)
174
- {
175
- var itemType = GetItemType(typeToConvert);
176
- if (itemType == null) return false;
177
- if (itemType != typeof(string) && typeof(IEnumerable).IsAssignableFrom(itemType)) return false;
178
- return typeToConvert.GetConstructor(Type.EmptyTypes) != null && !typeToConvert.IsValueType;
179
- }
180
-
181
- public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options)
182
- {
183
- var itemType = GetItemType(typeToConvert);
184
- var converterType = typeof(SingleOrArrayConverter<,>).MakeGenericType(typeToConvert, itemType);
185
- return (JsonConverter)Activator.CreateInstance(converterType, new object[] { CanWrite });
186
- }
187
-
188
- static Type GetItemType(Type type)
189
- {
190
- if (type.IsPrimitive || type.IsArray || type == typeof(string)) return null;
191
- while (type != null)
192
- {
193
- if (type.IsGenericType)
194
- {
195
- var genType = type.GetGenericTypeDefinition();
196
- if (genType == typeof(List<>)) return type.GetGenericArguments()[0];
197
- }
198
- type = type.BaseType;
199
- }
200
- return null;
201
- }
202
- }
203
-
204
166
  public class SingleOrArrayConverter<TCollection, TItem> : JsonConverter<TCollection> where TCollection : class, ICollection<TItem>, new()
205
167
  {
206
168
  public bool CanWrite { get; }

3

見直しキャンペーン中

2023/08/13 12:40

投稿

TN8001
TN8001

スコア10108

answer CHANGED
@@ -84,4 +84,160 @@
84
84
  public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) => throw new NotImplementedException();
85
85
  }
86
86
  }
87
+ ```
88
+
89
+ ---
90
+
91
+ System.Text.Json版
92
+
93
+ [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)
94
+ ```cs
95
+ using System;
96
+ using System.Collections;
97
+ using System.Collections.Generic;
98
+ using System.Linq;
99
+ using System.Text.Json;
100
+ using System.Text.Json.Serialization;
101
+
102
+ namespace Questions320367
103
+ {
104
+ class Program
105
+ {
106
+ static void Main()
107
+ {
108
+ var arrayJson = @"
109
+ {
110
+ ""LoginList"": {
111
+ ""LoginItem"": [
112
+ {
113
+ ""ID"": ""TEST01""
114
+ },
115
+ {
116
+ ""ID"": ""TEST02""
117
+ }
118
+ ]
119
+ }
120
+ }";
121
+ var result = JsonSerializer.Deserialize<Rootobject>(arrayJson);
122
+ List<Loginitem> loginItem = result.LoginList.LoginItem;
123
+
124
+ Console.WriteLine(loginItem.Count);
125
+ Console.WriteLine(string.Join(", ", loginItem.Select(x => x.ID)));
126
+
127
+
128
+ var singleJson = @"
129
+ {
130
+ ""LoginList"": {
131
+ ""LoginItem"": {
132
+ ""ID"": ""TEST01""
133
+ }
134
+ }
135
+ }";
136
+ result = JsonSerializer.Deserialize<Rootobject>(singleJson);
137
+ loginItem = result.LoginList.LoginItem;
138
+
139
+ Console.WriteLine(loginItem.Count);
140
+ Console.WriteLine(string.Join(", ", loginItem.Select(x => x.ID)));
141
+ }
142
+ }
143
+
144
+ public class Rootobject
145
+ {
146
+ public Loginlist LoginList { get; set; }
147
+ }
148
+ public class Loginlist
149
+ {
150
+ [JsonConverter(typeof(SingleOrArrayConverter<Loginitem>))]
151
+ public List<Loginitem> LoginItem { get; set; }
152
+ }
153
+ public class Loginitem
154
+ {
155
+ public string ID { get; set; }
156
+ }
157
+
158
+
159
+ // [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)
160
+ public class SingleOrArrayConverter<TItem> : SingleOrArrayConverter<List<TItem>, TItem>
161
+ {
162
+ public SingleOrArrayConverter() : this(true) { }
163
+ public SingleOrArrayConverter(bool canWrite) : base(canWrite) { }
164
+ }
165
+
166
+ public class SingleOrArrayConverterFactory : JsonConverterFactory
167
+ {
168
+ public bool CanWrite { get; }
169
+
170
+ public SingleOrArrayConverterFactory() : this(true) { }
171
+ public SingleOrArrayConverterFactory(bool canWrite) => CanWrite = canWrite;
172
+
173
+ public override bool CanConvert(Type typeToConvert)
174
+ {
175
+ var itemType = GetItemType(typeToConvert);
176
+ if (itemType == null) return false;
177
+ if (itemType != typeof(string) && typeof(IEnumerable).IsAssignableFrom(itemType)) return false;
178
+ return typeToConvert.GetConstructor(Type.EmptyTypes) != null && !typeToConvert.IsValueType;
179
+ }
180
+
181
+ public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options)
182
+ {
183
+ var itemType = GetItemType(typeToConvert);
184
+ var converterType = typeof(SingleOrArrayConverter<,>).MakeGenericType(typeToConvert, itemType);
185
+ return (JsonConverter)Activator.CreateInstance(converterType, new object[] { CanWrite });
186
+ }
187
+
188
+ static Type GetItemType(Type type)
189
+ {
190
+ if (type.IsPrimitive || type.IsArray || type == typeof(string)) return null;
191
+ while (type != null)
192
+ {
193
+ if (type.IsGenericType)
194
+ {
195
+ var genType = type.GetGenericTypeDefinition();
196
+ if (genType == typeof(List<>)) return type.GetGenericArguments()[0];
197
+ }
198
+ type = type.BaseType;
199
+ }
200
+ return null;
201
+ }
202
+ }
203
+
204
+ public class SingleOrArrayConverter<TCollection, TItem> : JsonConverter<TCollection> where TCollection : class, ICollection<TItem>, new()
205
+ {
206
+ public bool CanWrite { get; }
207
+
208
+ public SingleOrArrayConverter() : this(true) { }
209
+ public SingleOrArrayConverter(bool canWrite) => CanWrite = canWrite;
210
+
211
+ public override TCollection Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
212
+ {
213
+ switch (reader.TokenType)
214
+ {
215
+ case JsonTokenType.Null: return null;
216
+ case JsonTokenType.StartArray:
217
+ var list = new TCollection();
218
+ while (reader.Read())
219
+ {
220
+ if (reader.TokenType == JsonTokenType.EndArray) break;
221
+ list.Add(JsonSerializer.Deserialize<TItem>(ref reader, options));
222
+ }
223
+ return list;
224
+ default:
225
+ return new TCollection { JsonSerializer.Deserialize<TItem>(ref reader, options) };
226
+ }
227
+ }
228
+ public override void Write(Utf8JsonWriter writer, TCollection value, JsonSerializerOptions options)
229
+ {
230
+ if (CanWrite && value.Count == 1)
231
+ {
232
+ JsonSerializer.Serialize(writer, value.First(), options);
233
+ }
234
+ else
235
+ {
236
+ writer.WriteStartArray();
237
+ foreach (var item in value) JsonSerializer.Serialize(writer, item, options);
238
+ writer.WriteEndArray();
239
+ }
240
+ }
241
+ }
242
+ }
87
243
  ```

2

見直しキャンペーン中

2023/07/26 13:45

投稿

TN8001
TN8001

スコア10108

answer CHANGED
@@ -1,87 +1,87 @@
1
- 型が変わってしまったらただただ扱いづらいだけです。
2
- 実際にやりたいことは、どちらを読んでも`List<Loginitem>`になることなんじゃないですか?
3
-
4
- [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)
5
-
6
- ```C#
7
- using Newtonsoft.Json;
8
- using Newtonsoft.Json.Linq;
9
- using System;
10
- using System.Linq;
11
- using System.Collections.Generic;
12
-
13
- namespace Questions320367
14
- {
15
- class Program
16
- {
17
- static void Main()
18
- {
19
- var arrayJson = @"
20
- {
21
- ""LoginList"": {
22
- ""LoginItem"": [
23
- {
24
- ""ID"": ""TEST01""
25
- },
26
- {
27
- ""ID"": ""TEST02""
28
- }
29
- ]
30
- }
31
- }";
32
- var result = JsonConvert.DeserializeObject<Rootobject>(arrayJson);
33
- List<Loginitem> loginItem = result.LoginList.LoginItem;
34
-
35
- Console.WriteLine(loginItem.Count);
36
- Console.WriteLine(string.Join(", ", loginItem.Select(x => x.ID)));
37
-
38
-
39
- var singleJson = @"
40
- {
41
- ""LoginList"": {
42
- ""LoginItem"": {
43
- ""ID"": ""TEST01""
44
- }
45
- }
46
- }";
47
- result = JsonConvert.DeserializeObject<Rootobject>(singleJson);
48
- loginItem = result.LoginList.LoginItem;
49
-
50
- Console.WriteLine(loginItem.Count);
51
- Console.WriteLine(string.Join(", ", loginItem.Select(x => x.ID)));
52
- }
53
- }
54
-
55
- public class Rootobject
56
- {
57
- public Loginlist LoginList { get; set; }
58
- }
59
- public class Loginlist
60
- {
61
- [JsonConverter(typeof(SingleOrArrayConverter<Loginitem>))]
62
- public List<Loginitem> LoginItem { get; set; }
63
- }
64
- public class Loginitem
65
- {
66
- public string ID { get; set; }
67
- }
68
-
69
- // [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)
70
- internal class SingleOrArrayConverter<T> : JsonConverter
71
- {
72
- public override bool CanConvert(Type objectType) => objectType == typeof(List<T>);
73
- public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
74
- {
75
- var token = JToken.Load(reader);
76
- if (token.Type == JTokenType.Array)
77
- {
78
- return token.ToObject<List<T>>();
79
- }
80
- return new List<T> { token.ToObject<T>() };
81
- }
82
-
83
- public override bool CanWrite => false;
84
- public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) => throw new NotImplementedException();
85
- }
86
- }
1
+ 型が変わってしまったらただただ扱いづらいだけです。
2
+ 実際にやりたいことは、どちらを読んでも`List<Loginitem>`になることではないですか?
3
+
4
+ [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)
5
+
6
+ ```cs
7
+ using Newtonsoft.Json;
8
+ using Newtonsoft.Json.Linq;
9
+ using System;
10
+ using System.Linq;
11
+ using System.Collections.Generic;
12
+
13
+ namespace Questions320367
14
+ {
15
+ class Program
16
+ {
17
+ static void Main()
18
+ {
19
+ var arrayJson = @"
20
+ {
21
+ ""LoginList"": {
22
+ ""LoginItem"": [
23
+ {
24
+ ""ID"": ""TEST01""
25
+ },
26
+ {
27
+ ""ID"": ""TEST02""
28
+ }
29
+ ]
30
+ }
31
+ }";
32
+ var result = JsonConvert.DeserializeObject<Rootobject>(arrayJson);
33
+ List<Loginitem> loginItem = result.LoginList.LoginItem;
34
+
35
+ Console.WriteLine(loginItem.Count);
36
+ Console.WriteLine(string.Join(", ", loginItem.Select(x => x.ID)));
37
+
38
+
39
+ var singleJson = @"
40
+ {
41
+ ""LoginList"": {
42
+ ""LoginItem"": {
43
+ ""ID"": ""TEST01""
44
+ }
45
+ }
46
+ }";
47
+ result = JsonConvert.DeserializeObject<Rootobject>(singleJson);
48
+ loginItem = result.LoginList.LoginItem;
49
+
50
+ Console.WriteLine(loginItem.Count);
51
+ Console.WriteLine(string.Join(", ", loginItem.Select(x => x.ID)));
52
+ }
53
+ }
54
+
55
+ public class Rootobject
56
+ {
57
+ public Loginlist LoginList { get; set; }
58
+ }
59
+ public class Loginlist
60
+ {
61
+ [JsonConverter(typeof(SingleOrArrayConverter<Loginitem>))]
62
+ public List<Loginitem> LoginItem { get; set; }
63
+ }
64
+ public class Loginitem
65
+ {
66
+ public string ID { get; set; }
67
+ }
68
+
69
+ // [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)
70
+ internal class SingleOrArrayConverter<T> : JsonConverter
71
+ {
72
+ public override bool CanConvert(Type objectType) => objectType == typeof(List<T>);
73
+ public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
74
+ {
75
+ var token = JToken.Load(reader);
76
+ if (token.Type == JTokenType.Array)
77
+ {
78
+ return token.ToObject<List<T>>();
79
+ }
80
+ return new List<T> { token.ToObject<T>() };
81
+ }
82
+
83
+ public override bool CanWrite => false;
84
+ public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) => throw new NotImplementedException();
85
+ }
86
+ }
87
87
  ```

1

文修正

2021/02/04 09:28

投稿

TN8001
TN8001

スコア10108

answer CHANGED
@@ -1,4 +1,5 @@
1
+ 型が変わってしまったらただただ扱いづらいだけです。
1
- 型が変わってしまったらただただ扱いづらいだけですから、実際にやりたいことはどちらを読んでも`List<Loginitem>`になることなんじゃないですか?
2
+ 実際にやりたいことはどちらを読んでも`List<Loginitem>`になることなんじゃないですか?
2
3
 
3
4
  [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)
4
5