5
5
using System . Collections . Generic ;
6
6
using System . Diagnostics ;
7
7
using System . Diagnostics . CodeAnalysis ;
8
+ using System . Globalization ;
8
9
using System . Reflection ;
9
10
using System . Text ;
10
11
using System . Xml . Schema ;
@@ -122,7 +123,7 @@ private void WriteMember(object? o, object? choiceSource, ElementAccessor[] elem
122
123
}
123
124
else
124
125
{
125
- WriteElements ( o , elements , text , choice , writeAccessors , memberTypeDesc . IsNullable ) ;
126
+ WriteElements ( o , choiceSource , elements , text , choice , writeAccessors , memberTypeDesc . IsNullable ) ;
126
127
}
127
128
}
128
129
@@ -146,10 +147,10 @@ private void WriteArray(object o, object? choiceSource, ElementAccessor[] elemen
146
147
}
147
148
}
148
149
149
- WriteArrayItems ( elements , text , choice , o ) ;
150
+ WriteArrayItems ( elements , text , choice , o , choiceSource ) ;
150
151
}
151
152
152
- private void WriteArrayItems ( ElementAccessor [ ] elements , TextAccessor ? text , ChoiceIdentifierAccessor ? choice , object o )
153
+ private void WriteArrayItems ( ElementAccessor [ ] elements , TextAccessor ? text , ChoiceIdentifierAccessor ? choice , object o , object ? choiceSources )
153
154
{
154
155
var arr = o as IList ;
155
156
@@ -158,7 +159,8 @@ private void WriteArrayItems(ElementAccessor[] elements, TextAccessor? text, Cho
158
159
for ( int i = 0 ; i < arr . Count ; i ++ )
159
160
{
160
161
object ? ai = arr [ i ] ;
161
- WriteElements ( ai , elements , text , choice , true , true ) ;
162
+ var choiceSource = ( ( Array ? ) choiceSources ) ? . GetValue ( i ) ;
163
+ WriteElements ( ai , choiceSource , elements , text , choice , true , true ) ;
162
164
}
163
165
}
164
166
else
@@ -169,16 +171,18 @@ private void WriteArrayItems(ElementAccessor[] elements, TextAccessor? text, Cho
169
171
IEnumerator e = a . GetEnumerator ( ) ;
170
172
if ( e != null )
171
173
{
174
+ int c = 0 ;
172
175
while ( e . MoveNext ( ) )
173
176
{
174
177
object ai = e . Current ;
175
- WriteElements ( ai , elements , text , choice , true , true ) ;
178
+ var choiceSource = ( ( Array ? ) choiceSources ) ? . GetValue ( c ++ ) ;
179
+ WriteElements ( ai , choiceSource , elements , text , choice , true , true ) ;
176
180
}
177
181
}
178
182
}
179
183
}
180
184
181
- private void WriteElements ( object ? o , ElementAccessor [ ] elements , TextAccessor ? text , ChoiceIdentifierAccessor ? choice , bool writeAccessors , bool isNullable )
185
+ private void WriteElements ( object ? o , object ? choiceSource , ElementAccessor [ ] elements , TextAccessor ? text , ChoiceIdentifierAccessor ? choice , bool writeAccessors , bool isNullable )
182
186
{
183
187
if ( elements . Length == 0 && text == null )
184
188
return ;
@@ -216,16 +220,35 @@ private void WriteElements(object? o, ElementAccessor[] elements, TextAccessor?
216
220
}
217
221
else if ( choice != null )
218
222
{
219
- if ( o != null && o . GetType ( ) == element . Mapping ! . TypeDesc ! . Type )
223
+ // This looks heavy - getting names of enums in string form for comparison rather than just comparing values.
224
+ // But this faithfully mimics NetFx, and is necessary to prevent confusion between different enum types.
225
+ // ie EnumType.ValueX could == 1, but TotallyDifferentEnumType.ValueY could also == 1.
226
+ TypeDesc td = element . Mapping ! . TypeDesc ! ;
227
+ bool enumUseReflection = choice . Mapping ! . TypeDesc ! . UseReflection ;
228
+ string enumTypeName = choice . Mapping ! . TypeDesc ! . FullName ;
229
+ string enumFullName = ( enumUseReflection ? "" : enumTypeName + ".@" ) + FindChoiceEnumValue ( element , ( EnumMapping ) choice . Mapping , enumUseReflection ) ;
230
+ string choiceFullName = ( enumUseReflection ? "" : choiceSource ! . GetType ( ) . FullName + ".@" ) + choiceSource ! . ToString ( ) ;
231
+
232
+ if ( choiceFullName == enumFullName )
220
233
{
221
- WriteElement ( o , element , writeAccessors ) ;
222
- return ;
234
+ // Object is either non-null, or it is allowed to be null
235
+ if ( o != null || ( ! isNullable || element . IsNullable ) )
236
+ {
237
+ // But if Object is non-null, it's got to match types
238
+ if ( o != null && ! td . Type ! . IsAssignableFrom ( o ! . GetType ( ) ) )
239
+ {
240
+ throw CreateMismatchChoiceException ( td . FullName , choice . MemberName ! , enumFullName ) ;
241
+ }
242
+
243
+ WriteElement ( o , element , writeAccessors ) ;
244
+ return ;
245
+ }
223
246
}
224
247
}
225
248
else
226
249
{
227
250
TypeDesc td = element . IsUnbounded ? element . Mapping ! . TypeDesc ! . CreateArrayTypeDesc ( ) : element . Mapping ! . TypeDesc ! ;
228
- if ( o ! . GetType ( ) == td . Type )
251
+ if ( td . Type ! . IsAssignableFrom ( o ! . GetType ( ) ) )
229
252
{
230
253
WriteElement ( o , element , writeAccessors ) ;
231
254
return ;
@@ -274,6 +297,58 @@ private void WriteElements(object? o, ElementAccessor[] elements, TextAccessor?
274
297
}
275
298
}
276
299
300
+ private static string FindChoiceEnumValue ( ElementAccessor element , EnumMapping choiceMapping , bool useReflection )
301
+ {
302
+ string ? enumValue = null ;
303
+
304
+ for ( int i = 0 ; i < choiceMapping . Constants ! . Length ; i ++ )
305
+ {
306
+ string xmlName = choiceMapping . Constants [ i ] . XmlName ;
307
+
308
+ if ( element . Any && element . Name . Length == 0 )
309
+ {
310
+ if ( xmlName == "##any:" )
311
+ {
312
+ if ( useReflection )
313
+ enumValue = choiceMapping . Constants [ i ] . Value . ToString ( CultureInfo . InvariantCulture ) ;
314
+ else
315
+ enumValue = choiceMapping . Constants [ i ] . Name ;
316
+ break ;
317
+ }
318
+ continue ;
319
+ }
320
+ int colon = xmlName . LastIndexOf ( ':' ) ;
321
+ string ? choiceNs = colon < 0 ? choiceMapping . Namespace : xmlName . Substring ( 0 , colon ) ;
322
+ string choiceName = colon < 0 ? xmlName : xmlName . Substring ( colon + 1 ) ;
323
+
324
+ if ( element . Name == choiceName )
325
+ {
326
+ if ( ( element . Form == XmlSchemaForm . Unqualified && string . IsNullOrEmpty ( choiceNs ) ) || element . Namespace == choiceNs )
327
+ {
328
+ if ( useReflection )
329
+ enumValue = choiceMapping . Constants [ i ] . Value . ToString ( CultureInfo . InvariantCulture ) ;
330
+ else
331
+ enumValue = choiceMapping . Constants [ i ] . Name ;
332
+ break ;
333
+ }
334
+ }
335
+ }
336
+
337
+ if ( string . IsNullOrEmpty ( enumValue ) )
338
+ {
339
+ if ( element . Any && element . Name . Length == 0 )
340
+ {
341
+ // Type {0} is missing enumeration value '##any' for XmlAnyElementAttribute.
342
+ throw new InvalidOperationException ( SR . Format ( SR . XmlChoiceMissingAnyValue , choiceMapping . TypeDesc ! . FullName ) ) ;
343
+ }
344
+ // Type {0} is missing value for '{1}'.
345
+ throw new InvalidOperationException ( SR . Format ( SR . XmlChoiceMissingValue , choiceMapping . TypeDesc ! . FullName , element . Namespace + ":" + element . Name , element . Name , element . Namespace ) ) ;
346
+ }
347
+ if ( ! useReflection )
348
+ CodeIdentifier . CheckValidIdentifier ( enumValue ) ;
349
+ return enumValue ;
350
+ }
351
+
277
352
private void WriteText ( object o , TextAccessor text )
278
353
{
279
354
if ( text . Mapping is PrimitiveMapping primitiveMapping )
@@ -369,7 +444,7 @@ private void WriteElement(object? o, ElementAccessor element, bool writeAccessor
369
444
if ( o != null )
370
445
{
371
446
WriteStartElement ( name , ns , false ) ;
372
- WriteArrayItems ( mapping . ElementsSortedByDerivation ! , null , null , o ) ;
447
+ WriteArrayItems ( mapping . ElementsSortedByDerivation ! , null , null , o , null ) ;
373
448
WriteEndElement ( ) ;
374
449
}
375
450
}
0 commit comments