Skip to content

Commit

Permalink
Merge pull request #194 from AVTit/master
Browse files Browse the repository at this point in the history
Optimize unit tests, fix nullable baking field generation, add XmlRootAttribute for enums
  • Loading branch information
mganss authored May 3, 2020
2 parents 00c10ee + 0996162 commit 19a1472
Show file tree
Hide file tree
Showing 3 changed files with 191 additions and 12 deletions.
159 changes: 150 additions & 9 deletions XmlSchemaClassGenerator.Tests/XmlTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -342,9 +342,9 @@ private void TestSamples(string name, string pattern)
DeserializeSampleXml(pattern, assembly);
}

private bool HandleValidationError(string xml, ValidationEventArgs e)
private bool HandleValidationError(string[] xmlLines, ValidationEventArgs e)
{
var line = xml.Split('\n')[e.Exception.LineNumber - 1].Substring(e.Exception.LinePosition - 1);
var line = xmlLines[e.Exception.LineNumber - 1].Substring(e.Exception.LinePosition - 1);
var severity = e.Severity == XmlSeverityType.Error ? "Error" : "Warning";
Output.WriteLine($"{severity} at line {e.Exception.LineNumber}, column {e.Exception.LinePosition}: {e.Message}");
Output.WriteLine(line);
Expand All @@ -371,19 +371,19 @@ private void DeserializeSampleXml(string pattern, Assembly assembly)
set.Compile();

var anyValidXml = false;

var sb = new StringBuilder();
foreach (var rootElement in set.GlobalElements.Values.Cast<XmlSchemaElement>().Where(e => !e.IsAbstract && !(e.ElementSchemaType is XmlSchemaSimpleType)))
{
var type = FindType(assembly, rootElement.QualifiedName);
var serializer = new XmlSerializer(type);
var generator = new XmlSampleGenerator(set, rootElement.QualifiedName);
var sb = new StringBuilder();

using var xw = XmlWriter.Create(sb, new XmlWriterSettings { Indent = true });

// generate sample xml
generator.WriteXml(xw);
var xml = sb.ToString();

sb.Clear();
File.WriteAllText("xml.xml", xml);

// validate serialized xml
Expand All @@ -394,10 +394,10 @@ private void DeserializeSampleXml(string pattern, Assembly assembly)
};

var invalid = false;

var xmlLines = xml.Split('\n');
void validate(object s, ValidationEventArgs e)
{
if (HandleValidationError(xml, e)) invalid = true;
if (HandleValidationError(xmlLines, e)) invalid = true;
}

settings.ValidationEventHandler += validate;
Expand All @@ -419,10 +419,10 @@ void validate(object s, ValidationEventArgs e)
var xml2 = Serialize(serializer, o);

File.WriteAllText("xml2.xml", xml2);

xmlLines = xml2.Split('\n');
void validate2(object s, ValidationEventArgs e)
{
if (HandleValidationError(xml2, e)) throw e.Exception;
if (HandleValidationError(xmlLines, e)) throw e.Exception;
};

settings.ValidationEventHandler += validate2;
Expand Down Expand Up @@ -1582,5 +1582,146 @@ public void DoNotGenerateSamePropertiesInDerivedInterfacesClassTest()
Assert.NotNull(level3InterfacePropertyInfo);

}

[Fact]
public void NillableWithDefaultValueTest()
{
const string xsd = @"<?xml version=""1.0"" encoding=""UTF-8""?>
<xs:schema xmlns:xs=""http://www.w3.org/2001/XMLSchema""
elementFormDefault=""qualified"" attributeFormDefault=""unqualified"">
<xs:complexType name=""TestType"">
<xs:sequence>
<xs:element name=""IntProperty"" type=""xs:int"" nillable=""true"" default=""9000"" />
</xs:sequence>
</xs:complexType>
</xs:schema>";

var generator = new Generator
{
NamespaceProvider = new NamespaceProvider
{
GenerateNamespace = key => "Test"
}
};
var contents = ConvertXml(nameof(NillableWithDefaultValueTest), xsd, generator);
var content = Assert.Single(contents);

var assembly = Compiler.Compile(nameof(NillableWithDefaultValueTest), content);

var testType = assembly.GetType("Test.TestType");
Assert.NotNull(testType);

var propertyInfo = testType.GetProperties().FirstOrDefault(p => p.Name == "IntProperty");
Assert.NotNull(propertyInfo);
var testTypeInstance = Activator.CreateInstance(testType);
var propertyDefaultValue = propertyInfo.GetValue(testTypeInstance);
Assert.IsType<int>(propertyDefaultValue);
Assert.Equal(9000, propertyDefaultValue);

propertyInfo.SetValue(testTypeInstance, null);
var serializer = new XmlSerializer(testType);

var serializedXml = Serialize(serializer, testTypeInstance);
Assert.Contains(
@":nil=""true""",
serializedXml);
}

[Fact]
public void GenerateXmlRootAttributeForEnumTest()
{
const string xsd = @"<?xml version=""1.0"" encoding=""UTF-8""?>
<xs:schema xmlns:xs=""http://www.w3.org/2001/XMLSchema"" targetNamespace=""http://test.namespace""
elementFormDefault=""qualified"" attributeFormDefault=""unqualified"">
<xs:element name=""EnumTestType"">
<xs:simpleType>
<xs:restriction base=""xs:string"">
<xs:enumeration value=""EnumValue""/>
</xs:restriction>
</xs:simpleType>
</xs:element>
</xs:schema>";

var generator = new Generator
{
NamespaceProvider = new NamespaceProvider
{
GenerateNamespace = key => "Test"
}
};
var contents = ConvertXml(nameof(GenerateXmlRootAttributeForEnumTest), xsd, generator);
var content = Assert.Single(contents);

var assembly = Compiler.Compile(nameof(GenerateXmlRootAttributeForEnumTest), content);

var testType = assembly.GetType("Test.EnumTestType");
Assert.NotNull(testType);
var xmlRootAttribute = testType.GetCustomAttributes<XmlRootAttribute>().FirstOrDefault();
Assert.NotNull(xmlRootAttribute);
Assert.Equal("EnumTestType", xmlRootAttribute.ElementName);
Assert.Equal("http://test.namespace", xmlRootAttribute.Namespace);
}

[Fact]
public void AmbiguousTypesTest()
{
const string xsd1 = @"<?xml version=""1.0"" encoding=""UTF-8""?>
<xs:schema xmlns:xs=""http://www.w3.org/2001/XMLSchema"" targetNamespace=""Test_NS1""
elementFormDefault=""qualified"" attributeFormDefault=""unqualified"">
<xs:element name=""EnumTestType"">
<xs:simpleType>
<xs:restriction base=""xs:string"">
<xs:enumeration value=""EnumValue""/>
</xs:restriction>
</xs:simpleType>
</xs:element>
</xs:schema>";
const string xsd2 = @"<?xml version=""1.0"" encoding=""UTF-8""?>
<xs:schema xmlns:xs=""http://www.w3.org/2001/XMLSchema"" targetNamespace=""Test_NS2""
elementFormDefault=""qualified"" attributeFormDefault=""unqualified"">
<xs:element name=""EnumTestType"">
<xs:simpleType>
<xs:restriction base=""xs:string"">
<xs:enumeration value=""EnumValue""/>
</xs:restriction>
</xs:simpleType>
</xs:element>
</xs:schema>";
var generator = new Generator
{
NamespaceProvider = new NamespaceProvider
{
GenerateNamespace = key =>key.XmlSchemaNamespace
}
};
var contents1 = ConvertXml(nameof(GenerateXmlRootAttributeForEnumTest), xsd1, generator);
var contents2 = ConvertXml(nameof(GenerateXmlRootAttributeForEnumTest), xsd2, generator);
var content1 = Assert.Single(contents1);
var content2 = Assert.Single(contents2);

var assembly = Compiler.Compile(nameof(GenerateXmlRootAttributeForEnumTest), content1, content2);

var testType = assembly.GetType("Test_NS1.EnumTestType");
Assert.NotNull(testType);
var xmlRootAttribute = testType.GetCustomAttributes<XmlRootAttribute>().FirstOrDefault();
Assert.NotNull(xmlRootAttribute);
Assert.Equal("EnumTestType", xmlRootAttribute.ElementName);
Assert.Equal("Test_NS1", xmlRootAttribute.Namespace);

testType = assembly.GetType("Test_NS2.EnumTestType");
Assert.NotNull(testType);
xmlRootAttribute = testType.GetCustomAttributes<XmlRootAttribute>().FirstOrDefault();
Assert.NotNull(xmlRootAttribute);
Assert.Equal("EnumTestType", xmlRootAttribute.ElementName);
Assert.Equal("Test_NS2", xmlRootAttribute.Namespace);
}
}
}
19 changes: 19 additions & 0 deletions XmlSchemaClassGenerator/ModelBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,25 @@ public ModelBuilder(GeneratorConfiguration configuration, XmlSchemaSet set)
RenameInterfacePropertiesIfRenamedInDerivedClasses();
RemoveDuplicateInterfaceProperties();
}

AddXmlRootAttributeToAmbiguousTypes();
}

private void AddXmlRootAttributeToAmbiguousTypes()
{
var ambiguousTypes = Types.Values.Where(t=>t.RootElementName == null && !(t is InterfaceModel)).GroupBy(t => t.Name);
foreach (var ambiguousTypeGroup in ambiguousTypes)
{
var types = ambiguousTypeGroup.ToList();
if (types.Count == 1)
{
continue;
}
foreach (var typeModel in types)
{
typeModel.RootElementName = typeModel.GetQualifiedName();
}
}
}

private void RemoveDuplicateInterfaceProperties()
Expand Down
25 changes: 22 additions & 3 deletions XmlSchemaClassGenerator/TypeModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -831,10 +831,22 @@ public void AddMembersTo(CodeTypeDeclaration typeDeclaration, bool withDataBindi
var typeReference = TypeReference;

var requiresBackingField = withDataBinding || DefaultValue != null || IsCollection || isArray;
var backingField = new CodeMemberField(typeReference, OwningType.GetUniqueFieldName(this))
CodeMemberField backingField;

if (IsNillableValueType)
{
Attributes = MemberAttributes.Private
};
var nullableType = new CodeTypeReference(typeof(Nullable<>), Configuration.CodeTypeReferenceOptions);
nullableType.TypeArguments.Add(typeReference);
backingField = new CodeMemberField(nullableType, OwningType.GetUniqueFieldName(this));
}
else
{
backingField = new CodeMemberField(typeReference, OwningType.GetUniqueFieldName(this))
{
Attributes = MemberAttributes.Private
};
}

var ignoreAttribute = new CodeAttributeDeclaration(new CodeTypeReference(typeof(XmlIgnoreAttribute), Configuration.CodeTypeReferenceOptions));
var notMappedAttribute = new CodeAttributeDeclaration(new CodeTypeReference(typeof(NotMappedAttribute), Configuration.CodeTypeReferenceOptions));
backingField.CustomAttributes.Add(ignoreAttribute);
Expand Down Expand Up @@ -1300,6 +1312,13 @@ public override CodeTypeDeclaration Generate()
enumDeclaration.Members.Add(member);
}

if (RootElementName != null)
{
var rootAttribute = new CodeAttributeDeclaration(new CodeTypeReference(typeof(XmlRootAttribute), Configuration.CodeTypeReferenceOptions),
new CodeAttributeArgument(new CodePrimitiveExpression(RootElementName.Name)),
new CodeAttributeArgument("Namespace", new CodePrimitiveExpression(RootElementName.Namespace)));
enumDeclaration.CustomAttributes.Add(rootAttribute);
}
Configuration.TypeVisitor(enumDeclaration, this);
return enumDeclaration;
}
Expand Down

0 comments on commit 19a1472

Please sign in to comment.