Skip to content

Commit 22da919

Browse files
committed
Add OmitXmlIncludeAttribute (fixes #567)
1 parent 9254358 commit 22da919

6 files changed

Lines changed: 231 additions & 6 deletions

File tree

XmlSchemaClassGenerator.Console/Program.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ static int Main(string[] args)
6868
var separateNamespaceHierarchy = false;
6969
var serializeEmptyCollections = false;
7070
var allowDtdParse = false;
71+
var omitXmlIncludeAttribute = false;
7172
NamingScheme? namingScheme = null;
7273
var forceUriScheme = "none";
7374

@@ -167,6 +168,7 @@ A file name may be given by appending a pipe sign (|) followed by a file name (l
167168
{ "uc|unionCommonType", "generate a common type for unions if possible (default is false)", v => unionCommonType = v != null },
168169
{ "ec|serializeEmptyCollections", "serialize empty collections (default is false)", v => serializeEmptyCollections = v != null },
169170
{ "dtd|allowDtdParse", "allows dtd parse (default is false)", v => allowDtdParse = v != null },
171+
{ "oxi|omitXmlIncludeAttribute", "omit generation of XmlIncludeAttribute for derived types (default is false)", v => omitXmlIncludeAttribute = v != null },
170172
{ "ns|namingScheme=", @"use the specified naming scheme for class and property names (default is Pascal; can be: Direct, Pascal, Legacy)",
171173
v =>
172174
{
@@ -267,6 +269,7 @@ A file name may be given by appending a pipe sign (|) followed by a file name (l
267269
SeparateNamespaceHierarchy = separateNamespaceHierarchy,
268270
SerializeEmptyCollections = serializeEmptyCollections,
269271
AllowDtdParse = allowDtdParse,
272+
OmitXmlIncludeAttribute = omitXmlIncludeAttribute,
270273
ForceUriScheme = forceUriScheme
271274
};
272275

XmlSchemaClassGenerator.Tests/Compiler.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,8 @@ public static Assembly GenerateFiles(string name, IEnumerable<string> files, Gen
118118
UseArrayItemAttribute = generatorPrototype.UseArrayItemAttribute,
119119
EnumAsString = generatorPrototype.EnumAsString,
120120
MapUnionToWidestCommonType = generatorPrototype.MapUnionToWidestCommonType,
121-
SeparateNamespaceHierarchy = generatorPrototype.SeparateNamespaceHierarchy
121+
SeparateNamespaceHierarchy = generatorPrototype.SeparateNamespaceHierarchy,
122+
OmitXmlIncludeAttribute = generatorPrototype.OmitXmlIncludeAttribute,
122123
};
123124

124125
gen.CommentLanguages.Clear();

XmlSchemaClassGenerator.Tests/XmlTests.cs

Lines changed: 208 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@ private static IEnumerable<string> ConvertXml(string name, IEnumerable<string> x
5858
CollectionSettersMode = generatorPrototype.CollectionSettersMode,
5959
UseArrayItemAttribute = generatorPrototype.UseArrayItemAttribute,
6060
EnumAsString = generatorPrototype.EnumAsString,
61-
AllowDtdParse = generatorPrototype.AllowDtdParse
61+
AllowDtdParse = generatorPrototype.AllowDtdParse,
62+
OmitXmlIncludeAttribute = generatorPrototype.OmitXmlIncludeAttribute
6263
};
6364

6465
gen.CommentLanguages.Clear();
@@ -3082,4 +3083,210 @@ List of Profiles
30823083
var exception = Assert.Throws<XmlException>(() => ConvertXml(nameof(TestNotAllowDtdParse), xsd, generator));
30833084
Assert.Contains("Reference to undeclared entity 'lowalpha'", exception.Message);
30843085
}
3086+
3087+
[Fact]
3088+
public void TestOmitXmlIncludeAttribute()
3089+
{
3090+
const string xsd = @"<?xml version=""1.0"" encoding=""UTF-8""?>
3091+
<xs:schema xmlns:xs=""http://www.w3.org/2001/XMLSchema"" xmlns:xlink=""http://www.w3.org/1999/xlink"" elementFormDefault=""qualified"" attributeFormDefault=""unqualified"">
3092+
<xs:complexType name=""BaseType"">
3093+
<xs:sequence>
3094+
<xs:element name=""BaseProperty"" type=""xs:string""/>
3095+
</xs:sequence>
3096+
</xs:complexType>
3097+
<xs:complexType name=""DerivedType1"">
3098+
<xs:complexContent>
3099+
<xs:extension base=""BaseType"">
3100+
<xs:sequence>
3101+
<xs:element name=""DerivedProperty1"" type=""xs:string""/>
3102+
</xs:sequence>
3103+
</xs:extension>
3104+
</xs:complexContent>
3105+
</xs:complexType>
3106+
<xs:complexType name=""DerivedType2"">
3107+
<xs:complexContent>
3108+
<xs:extension base=""BaseType"">
3109+
<xs:sequence>
3110+
<xs:element name=""DerivedProperty2"" type=""xs:string""/>
3111+
</xs:sequence>
3112+
</xs:extension>
3113+
</xs:complexContent>
3114+
</xs:complexType>
3115+
</xs:schema>";
3116+
3117+
// Test with OmitXmlIncludeAttribute = false (default behavior)
3118+
var generatorWithInclude = new Generator
3119+
{
3120+
NamespaceProvider = new NamespaceProvider
3121+
{
3122+
GenerateNamespace = key => "Test"
3123+
},
3124+
OmitXmlIncludeAttribute = false
3125+
};
3126+
3127+
var contentsWithInclude = ConvertXml(nameof(TestOmitXmlIncludeAttribute) + "WithInclude", xsd, generatorWithInclude);
3128+
var contentWithInclude = Assert.Single(contentsWithInclude);
3129+
3130+
// Verify XmlIncludeAttribute is present for both derived types
3131+
Assert.Contains("[System.Xml.Serialization.XmlIncludeAttribute(typeof(DerivedType1))]", contentWithInclude);
3132+
Assert.Contains("[System.Xml.Serialization.XmlIncludeAttribute(typeof(DerivedType2))]", contentWithInclude);
3133+
3134+
// Test with OmitXmlIncludeAttribute = true
3135+
var generatorWithoutInclude = new Generator
3136+
{
3137+
NamespaceProvider = new NamespaceProvider
3138+
{
3139+
GenerateNamespace = key => "Test"
3140+
},
3141+
OmitXmlIncludeAttribute = true
3142+
};
3143+
3144+
var contentsWithoutInclude = ConvertXml(nameof(TestOmitXmlIncludeAttribute) + "WithoutInclude", xsd, generatorWithoutInclude);
3145+
var contentWithoutInclude = Assert.Single(contentsWithoutInclude);
3146+
3147+
// Verify XmlIncludeAttribute is NOT present
3148+
Assert.DoesNotContain("XmlIncludeAttribute", contentWithoutInclude);
3149+
3150+
// Verify that the base and derived types are still generated correctly
3151+
Assert.Contains("public partial class BaseType", contentWithoutInclude);
3152+
Assert.Contains("public partial class DerivedType1 : BaseType", contentWithoutInclude);
3153+
Assert.Contains("public partial class DerivedType2 : BaseType", contentWithoutInclude);
3154+
}
3155+
3156+
[Fact]
3157+
public void TestOmitXmlIncludeAttributeSerializationWithExtraTypes()
3158+
{
3159+
const string xsd = @"<?xml version=""1.0"" encoding=""UTF-8""?>
3160+
<xs:schema xmlns:xs=""http://www.w3.org/2001/XMLSchema"" targetNamespace=""http://test.example/omit"" xmlns:t=""http://test.example/omit"" elementFormDefault=""qualified"">
3161+
<xs:element name=""Container"" type=""t:ContainerType""/>
3162+
<xs:complexType name=""ContainerType"">
3163+
<xs:sequence>
3164+
<xs:element name=""Item"" type=""t:BaseType""/>
3165+
</xs:sequence>
3166+
</xs:complexType>
3167+
<xs:complexType name=""BaseType"">
3168+
<xs:sequence>
3169+
<xs:element name=""BaseProperty"" type=""xs:string""/>
3170+
</xs:sequence>
3171+
</xs:complexType>
3172+
<xs:complexType name=""DerivedType1"">
3173+
<xs:complexContent>
3174+
<xs:extension base=""t:BaseType"">
3175+
<xs:sequence>
3176+
<xs:element name=""DerivedProperty1"" type=""xs:string""/>
3177+
</xs:sequence>
3178+
</xs:extension>
3179+
</xs:complexContent>
3180+
</xs:complexType>
3181+
<xs:complexType name=""DerivedType2"">
3182+
<xs:complexContent>
3183+
<xs:extension base=""t:BaseType"">
3184+
<xs:sequence>
3185+
<xs:element name=""DerivedProperty2"" type=""xs:int""/>
3186+
</xs:sequence>
3187+
</xs:extension>
3188+
</xs:complexContent>
3189+
</xs:complexType>
3190+
</xs:schema>";
3191+
3192+
var generator = new Generator
3193+
{
3194+
NamespaceProvider = new NamespaceProvider
3195+
{
3196+
GenerateNamespace = key => "Test.Omit"
3197+
},
3198+
OmitXmlIncludeAttribute = true
3199+
};
3200+
3201+
var contents = ConvertXml(nameof(TestOmitXmlIncludeAttributeSerializationWithExtraTypes), xsd, generator).ToArray();
3202+
var assembly = Compiler.Compile(nameof(TestOmitXmlIncludeAttributeSerializationWithExtraTypes), contents);
3203+
3204+
Assert.NotNull(assembly);
3205+
3206+
// Get the generated types
3207+
var containerType = assembly.GetType("Test.Omit.ContainerType");
3208+
var baseType = assembly.GetType("Test.Omit.BaseType");
3209+
var derivedType1 = assembly.GetType("Test.Omit.DerivedType1");
3210+
var derivedType2 = assembly.GetType("Test.Omit.DerivedType2");
3211+
3212+
Assert.NotNull(containerType);
3213+
Assert.NotNull(baseType);
3214+
Assert.NotNull(derivedType1);
3215+
Assert.NotNull(derivedType2);
3216+
3217+
// Verify that BaseType does NOT have XmlIncludeAttribute
3218+
var xmlIncludeAttributes = baseType.GetCustomAttributes(typeof(XmlIncludeAttribute), false);
3219+
Assert.Empty(xmlIncludeAttributes);
3220+
3221+
// Test serialization with extraTypes parameter for DerivedType1
3222+
var serializer1 = new XmlSerializer(containerType, new[] { derivedType1, derivedType2 });
3223+
3224+
// Create an instance with DerivedType1
3225+
var container1 = Activator.CreateInstance(containerType);
3226+
var derived1 = Activator.CreateInstance(derivedType1);
3227+
3228+
var basePropertyProp = baseType.GetProperty("BaseProperty");
3229+
var derivedProperty1Prop = derivedType1.GetProperty("DerivedProperty1");
3230+
3231+
basePropertyProp.SetValue(derived1, "Base Value 1");
3232+
derivedProperty1Prop.SetValue(derived1, "Derived Value 1");
3233+
3234+
var itemProp = containerType.GetProperty("Item");
3235+
itemProp.SetValue(container1, derived1);
3236+
3237+
// Serialize
3238+
var sw1 = new StringWriter();
3239+
serializer1.Serialize(sw1, container1);
3240+
var xml1 = sw1.ToString();
3241+
3242+
// Verify the XML contains xsi:type information
3243+
Assert.Contains("DerivedType1", xml1);
3244+
Assert.Contains("Base Value 1", xml1);
3245+
Assert.Contains("Derived Value 1", xml1);
3246+
3247+
// Deserialize back
3248+
var sr1 = new StringReader(xml1);
3249+
var deserialized1 = serializer1.Deserialize(sr1);
3250+
3251+
Assert.NotNull(deserialized1);
3252+
var deserializedItem1 = itemProp.GetValue(deserialized1);
3253+
Assert.NotNull(deserializedItem1);
3254+
Assert.Equal(derivedType1, deserializedItem1.GetType());
3255+
Assert.Equal("Base Value 1", basePropertyProp.GetValue(deserializedItem1));
3256+
Assert.Equal("Derived Value 1", derivedProperty1Prop.GetValue(deserializedItem1));
3257+
3258+
// Test serialization with DerivedType2
3259+
var serializer2 = new XmlSerializer(containerType, new[] { derivedType1, derivedType2 });
3260+
3261+
var container2 = Activator.CreateInstance(containerType);
3262+
var derived2 = Activator.CreateInstance(derivedType2);
3263+
3264+
var derivedProperty2Prop = derivedType2.GetProperty("DerivedProperty2");
3265+
3266+
basePropertyProp.SetValue(derived2, "Base Value 2");
3267+
derivedProperty2Prop.SetValue(derived2, 42);
3268+
3269+
itemProp.SetValue(container2, derived2);
3270+
3271+
// Serialize
3272+
var sw2 = new StringWriter();
3273+
serializer2.Serialize(sw2, container2);
3274+
var xml2 = sw2.ToString();
3275+
3276+
// Verify the XML contains xsi:type information
3277+
Assert.Contains("DerivedType2", xml2);
3278+
Assert.Contains("Base Value 2", xml2);
3279+
Assert.Contains("42", xml2);
3280+
3281+
// Deserialize back
3282+
var sr2 = new StringReader(xml2);
3283+
var deserialized2 = serializer2.Deserialize(sr2);
3284+
3285+
Assert.NotNull(deserialized2);
3286+
var deserializedItem2 = itemProp.GetValue(deserialized2);
3287+
Assert.NotNull(deserializedItem2);
3288+
Assert.Equal(derivedType2, deserializedItem2.GetType());
3289+
Assert.Equal("Base Value 2", basePropertyProp.GetValue(deserializedItem2));
3290+
Assert.Equal(42, derivedProperty2Prop.GetValue(deserializedItem2));
3291+
}
30853292
}

XmlSchemaClassGenerator/Generator.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,12 @@ public bool AllowDtdParse
395395
set { _configuration.AllowDtdParse = value; }
396396
}
397397

398+
public bool OmitXmlIncludeAttribute
399+
{
400+
get { return _configuration.OmitXmlIncludeAttribute; }
401+
set { _configuration.OmitXmlIncludeAttribute = value; }
402+
}
403+
398404
public bool ValidationError { get; private set; }
399405

400406
static Generator()

XmlSchemaClassGenerator/GeneratorConfiguration.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,4 +361,9 @@ public void WriteLog(string message)
361361
/// Allow DTD parsing. Default is false.
362362
/// </summary>
363363
public bool AllowDtdParse { get; set; } = false;
364+
365+
/// <summary>
366+
/// Omit generation of XmlIncludeAttribute for derived types. Default is false.
367+
/// </summary>
368+
public bool OmitXmlIncludeAttribute { get; set; } = false;
364369
}

XmlSchemaClassGenerator/TypeModel.cs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -417,9 +417,12 @@ public override CodeTypeDeclaration Generate()
417417
customAttributes.Add(rootAttribute);
418418
}
419419

420-
var derivedTypes = GetAllDerivedTypes();
421-
foreach (var derivedType in derivedTypes.OrderBy(t => t.Name))
422-
customAttributes.Add(AttributeDecl<XmlIncludeAttribute>(new CodeAttributeArgument(new CodeTypeOfExpression(derivedType.GetReferenceFor(Namespace)))));
420+
if (!Configuration.OmitXmlIncludeAttribute)
421+
{
422+
var derivedTypes = GetAllDerivedTypes();
423+
foreach (var derivedType in derivedTypes.OrderBy(t => t.Name))
424+
customAttributes.Add(AttributeDecl<XmlIncludeAttribute>(new CodeAttributeArgument(new CodeTypeOfExpression(derivedType.GetReferenceFor(Namespace)))));
425+
}
423426

424427
classDeclaration.BaseTypes.AddRange(Interfaces.Select(i => i.GetReferenceFor(Namespace)).ToArray());
425428

@@ -760,7 +763,7 @@ public void AddMembersTo(CodeTypeDeclaration typeDeclaration, bool withDataBindi
760763
if (IsRequired && Configuration.DataAnnotationMode != DataAnnotationMode.None)
761764
{
762765
var requiredAttribute = new CodeAttributeDeclaration(CodeUtilities.CreateTypeReference(Attributes.Required, Configuration));
763-
var noEmptyStrings = propertyType is SimpleModel simpleModel
766+
var noEmptyStrings = propertyType is SimpleModel simpleModel
764767
&& simpleModel.Restrictions.Any(r => r is MinLengthRestrictionModel m && m.Value > 0);
765768
var allowEmptyStringsArgument = new CodeAttributeArgument("AllowEmptyStrings", new CodePrimitiveExpression(!noEmptyStrings));
766769
requiredAttribute.Arguments.Add(allowEmptyStringsArgument);

0 commit comments

Comments
 (0)