Skip to content

Commit

Permalink
Merge pull request #459 from fgheysels/frgh/feat/171_458_datetimeoffset
Browse files Browse the repository at this point in the history
Allow generation of DateTimeOffset properties for xs:dateTime
  • Loading branch information
mganss authored Dec 11, 2023
2 parents 9a5aa49 + c659d2e commit 8c1c5b1
Show file tree
Hide file tree
Showing 6 changed files with 96 additions and 1 deletion.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ Options:
Lines starting with # and empty lines are
ignored.
-o, --output=FOLDER the FOLDER to write the resulting .cs files to
-d, --datetime-offset map x:datetime to System.DateTimeOffset
instead of System.DateTime
-i, --integer=TYPE map xs:integer and derived types to TYPE instead
of automatic approximation
TYPE can be i[nt], l[ong], or d[ecimal]
Expand Down
3 changes: 3 additions & 0 deletions XmlSchemaClassGenerator.Console/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ static void Main(string[] args)
var namespaces = new List<string>();
var nameSubstitutes = new List<string>();
var outputFolder = (string)null;
bool dateTimeWithTimeZone = false;
Type integerType = null;
var useIntegerTypeAsFallback = false;
var namespacePrefix = "";
Expand Down Expand Up @@ -84,6 +85,7 @@ A file name may be given by appending a pipe sign (|) followed by a file name (l
The line format is one mapping per line: prefixed type/member name = substitute name.
Lines starting with # and empty lines are ignored.", v => nameSubstituteFiles.Add(v) },
{ "o|output=", "the {FOLDER} to write the resulting .cs files to", v => outputFolder = v },
{ "d|datetime-offset", "map xs:datetime and derived types to System.DateTimeOffset instead of System.DateTime", v => dateTimeWithTimeZone = v != null },
{ "i|integer=", @"map xs:integer and derived types to {TYPE} instead of automatic approximation
{TYPE} can be i[nt], l[ong], or d[ecimal]", v => {
switch (v)
Expand Down Expand Up @@ -210,6 +212,7 @@ A file name may be given by appending a pipe sign (|) followed by a file name (l
EmitOrder = emitOrder,
IntegerDataType = integerType,
UseIntegerDataTypeAsFallback = useIntegerTypeAsFallback,
DateTimeWithTimeZone = dateTimeWithTimeZone,
EntityFramework = entityFramework,
GenerateInterfaces = interfaces,
NamingScheme = pascal ? NamingScheme.PascalCase : NamingScheme.Direct,
Expand Down
80 changes: 80 additions & 0 deletions XmlSchemaClassGenerator.Tests/DateTimeTypeTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Schema;
using Xunit;

namespace XmlSchemaClassGenerator.Tests
{
public sealed class DateTimeTypeTests
{
private static IEnumerable<string> ConvertXml(string xsd, Generator generatorPrototype)
{
var writer = new MemoryOutputWriter();

var gen = new Generator
{
OutputWriter = writer,
Version = new("Tests", "1.0.0.1"),
NamespaceProvider = generatorPrototype.NamespaceProvider,
GenerateNullables = generatorPrototype.GenerateNullables,
DateTimeWithTimeZone = generatorPrototype.DateTimeWithTimeZone,
DataAnnotationMode = generatorPrototype.DataAnnotationMode,
GenerateDesignerCategoryAttribute = generatorPrototype.GenerateDesignerCategoryAttribute,
GenerateComplexTypesForCollections = generatorPrototype.GenerateComplexTypesForCollections,
EntityFramework = generatorPrototype.EntityFramework,
AssemblyVisible = generatorPrototype.AssemblyVisible,
GenerateInterfaces = generatorPrototype.GenerateInterfaces,
MemberVisitor = generatorPrototype.MemberVisitor,
CodeTypeReferenceOptions = generatorPrototype.CodeTypeReferenceOptions
};

var set = new XmlSchemaSet();

using (var stringReader = new StringReader(xsd))
{
var schema = XmlSchema.Read(stringReader, (_, e) => throw new InvalidOperationException($"{e.Severity}: {e.Message}", e.Exception));
ArgumentNullException.ThrowIfNull(schema);
set.Add(schema);
}

gen.Generate(set);

return writer.Content;
}

[Theory]
[InlineData(false, "System.DateTime")]
[InlineData(true, "System.DateTimeOffset")]
public void TestFallbackType(bool dateTimeWithTimeZone, string expectedType)
{
var xsd = @$"<?xml version=""1.0"" encoding=""UTF-8""?>
<xs:schema elementFormDefault=""qualified"" xmlns:xs=""http://www.w3.org/2001/XMLSchema"">
<xs:complexType name=""document"">
<xs:sequence>
<xs:element name=""someDate"" type=""xs:dateTime"" />
</xs:sequence>
</xs:complexType>
</xs:schema>";

var generatedType = ConvertXml(
xsd, new()
{
NamespaceProvider = new()
{
GenerateNamespace = _ => "Test"
},
DateTimeWithTimeZone = dateTimeWithTimeZone
});

var expectedProperty = $"public {expectedType} SomeDate";
var generatedProperty = generatedType.First();

Assert.Contains(expectedProperty, generatedProperty);
}

}
}
2 changes: 1 addition & 1 deletion XmlSchemaClassGenerator/CodeUtilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ public static Type GetEffectiveType(this XmlSchemaDatatype type, GeneratorConfig
XmlTypeCode.AnyAtomicType => configuration.MapUnionToWidestCommonType ? GetUnionType(configuration, schemaType, attribute) : typeof(string), // union
XmlTypeCode.AnyUri or XmlTypeCode.GDay or XmlTypeCode.GMonth or XmlTypeCode.GMonthDay or XmlTypeCode.GYear or XmlTypeCode.GYearMonth => typeof(string),
XmlTypeCode.Duration => configuration.NetCoreSpecificCode ? type.ValueType : typeof(string),
XmlTypeCode.Time => typeof(DateTime),
XmlTypeCode.Time or XmlTypeCode.DateTime => configuration.DateTimeWithTimeZone ? typeof(DateTimeOffset) : typeof(DateTime),
XmlTypeCode.Idref => typeof(string),
XmlTypeCode.Integer or XmlTypeCode.NegativeInteger or XmlTypeCode.NonNegativeInteger or XmlTypeCode.NonPositiveInteger or XmlTypeCode.PositiveInteger => GetIntegerDerivedType(type, configuration, restrictions),
XmlTypeCode.Decimal when restrictions.OfType<FractionDigitsRestrictionModel>().SingleOrDefault() is { IsSupported: true, Value: 0 } => GetIntegerDerivedType(type, configuration, restrictions),
Expand Down
6 changes: 6 additions & 0 deletions XmlSchemaClassGenerator/Generator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,12 @@ public bool UseIntegerDataTypeAsFallback
set { _configuration.UseIntegerDataTypeAsFallback = value; }
}

public bool DateTimeWithTimeZone
{
get { return _configuration.DateTimeWithTimeZone; }
set { _configuration.DateTimeWithTimeZone = value; }
}

public bool EntityFramework
{
get { return _configuration.EntityFramework; }
Expand Down
4 changes: 4 additions & 0 deletions XmlSchemaClassGenerator/GeneratorConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,10 @@ public NamingScheme NamingScheme
/// </summary>
public bool UseIntegerDataTypeAsFallback { get; set; }
/// <summary>
/// Generate DateTimeOffset properties for xs:dateTime elements
/// </summary>
public bool DateTimeWithTimeZone { get; set; } = false;
/// <summary>
/// Generate Entity Framework Code First compatible classes
/// </summary>
public bool EntityFramework { get; set; }
Expand Down

0 comments on commit 8c1c5b1

Please sign in to comment.