Skip to content
This repository was archived by the owner on Jun 10, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
190 changes: 117 additions & 73 deletions src/Json.Schema.ToDotNet.UnitTests/DataModelGeneratorTests.cs

Large diffs are not rendered by default.

14 changes: 7 additions & 7 deletions src/Json.Schema.ToDotNet.UnitTests/Hints/AttributeHintTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public partial class C
{
[DataMember(Name = ""theProperty"", IsRequired = false, EmitDefaultValue = false)]
[Test]
public int TheProperty { get; set; }
public int? TheProperty { get; set; }
}
}"
),
Expand Down Expand Up @@ -85,7 +85,7 @@ public partial class C
{
[DataMember(Name = ""theProperty"", IsRequired = false, EmitDefaultValue = false)]
[Test(typeof(string))]
public int TheProperty { get; set; }
public int? TheProperty { get; set; }
}
}"
),
Expand Down Expand Up @@ -129,7 +129,7 @@ public partial class C
{
[DataMember(Name = ""theProperty"", IsRequired = false, EmitDefaultValue = false)]
[Test(typeof(string), 42, ""a"")]
public int TheProperty { get; set; }
public int? TheProperty { get; set; }
}
}"
),
Expand Down Expand Up @@ -171,7 +171,7 @@ public partial class C
{
[DataMember(Name = ""theProperty"", IsRequired = false, EmitDefaultValue = false)]
[Test(DefaultValueHandling = DefaultValueHandling.Ignore)]
public int TheProperty { get; set; }
public int? TheProperty { get; set; }
}
}"
),
Expand Down Expand Up @@ -214,7 +214,7 @@ public partial class C
{
[DataMember(Name = ""theProperty"", IsRequired = false, EmitDefaultValue = false)]
[Test(DefaultValueHandling = DefaultValueHandling.Ignore, DefaultValue = 42)]
public int TheProperty { get; set; }
public int? TheProperty { get; set; }
}
}"
),
Expand Down Expand Up @@ -260,7 +260,7 @@ public partial class C
[DataMember(Name = ""theProperty"", IsRequired = false, EmitDefaultValue = false)]
[Test1]
[Test2]
public int TheProperty { get; set; }
public int? TheProperty { get; set; }
}
}"
),
Expand Down Expand Up @@ -301,7 +301,7 @@ public partial class C
{
[DataMember(Name = ""theProperty"", IsRequired = false, EmitDefaultValue = false)]
[Test]
public int TheProperty { get; set; }
public int? TheProperty { get; set; }
}
}"
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,13 @@ public partial class C : IC
/// The value.
/// </summary>
[DataMember(Name = ""value"", IsRequired = false, EmitDefaultValue = false)]
public int Value { get; set; }
public int? Value { get; set; }

/// <summary>
/// Internal value.
/// </summary>
[DataMember(Name = ""value2"", IsRequired = false, EmitDefaultValue = false)]
internal int Value2 { get; set; }
internal int? Value2 { get; set; }
}
}",

Expand All @@ -92,7 +92,7 @@ public partial interface IC
/// <summary>
/// The value.
/// </summary>
int Value { get; }
int? Value { get; }
}
}"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ namespace N
public partial class C
{
[DataMember(Name = ""theProperty"", IsRequired = false, EmitDefaultValue = false)]
int TheProperty { get; set; }
int? TheProperty { get; set; }
}
}"
),
Expand Down Expand Up @@ -83,7 +83,7 @@ namespace N
public partial class C
{
[DataMember(Name = ""theProperty"", IsRequired = false, EmitDefaultValue = false)]
internal int TheProperty { get; set; }
internal int? TheProperty { get; set; }
}
}"
),
Expand Down Expand Up @@ -124,7 +124,7 @@ namespace N
public partial class C
{
[DataMember(Name = ""theProperty"", IsRequired = false, EmitDefaultValue = false)]
internal override int TheProperty { get; set; }
internal override int? TheProperty { get; set; }
}
}"
),
Expand Down Expand Up @@ -194,7 +194,7 @@ namespace N
public partial class C
{
[DataMember(Name = ""theProperty"", IsRequired = false, EmitDefaultValue = false)]
internal override int TheProperty { get; set; }
internal override int? TheProperty { get; set; }
}
}"
),
Expand Down
42 changes: 29 additions & 13 deletions src/Json.Schema.ToDotNet/PropertyInfoDictionary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ public class PropertyInfoDictionary : IReadOnlyDictionary<string, PropertyInfo>
// <code>Location{}[]</code> is a dictionary property whose elements are arrays.
internal const string DictionaryMarker = "{}";

internal const string IntType = "System.int";
internal const string LongType = "System.long";
internal const string GuidType = "System.Guid";
internal const string BigIntegerType = "System.Numerics.BigInteger";

/// <summary>
Expand Down Expand Up @@ -234,6 +237,10 @@ private void AddPropertyInfoFromPropertySchema(
bool isOfSchemaDefinedType = false;
int arrayRank = 0;

bool scalarValueTypeNullable = !isRequired && propertySchema.Default == null;
HashKind scalarValueTypeHashKind =
scalarValueTypeNullable ? HashKind.ScalarReferenceType : HashKind.ScalarValueType;

if (propertySchema.IsDateTime())
{
comparisonKind = ComparisonKind.OperatorEquals;
Expand All @@ -251,9 +258,9 @@ private void AddPropertyInfoFromPropertySchema(
else if (propertySchema.IsUuid())
{
comparisonKind = ComparisonKind.OperatorEquals;
hashKind = isRequired ? HashKind.ScalarValueType : HashKind.ScalarReferenceType;
hashKind = scalarValueTypeHashKind;
initializationKind = InitializationKind.SimpleAssign;
type = MakeNamedType(isRequired ? "System.Guid" : "System.Guid?", out namespaceName);
type = MakeNamedType(GuidType, out namespaceName, scalarValueTypeNullable);
}
else if (propertySchema.ShouldBeDictionary(_typeName, schemaPropertyName, _hintDictionary, out DictionaryHint dictionaryHint))
{
Expand All @@ -280,7 +287,7 @@ private void AddPropertyInfoFromPropertySchema(
initializationKind = InitializationKind.SimpleAssign;
type = MakeNamedType(referencedEnumTypeName, out namespaceName);
}
else if (propertySchema.ShouldBeEnum(_typeName, schemaPropertyName, _hintDictionary, out EnumHint enumHint))
else if (propertySchema.ShouldBeEnum(_typeName, schemaPropertyName, _hintDictionary, out EnumHint enumHint))
{
comparisonKind = ComparisonKind.OperatorEquals;
hashKind = HashKind.ScalarValueType;
Expand Down Expand Up @@ -313,14 +320,15 @@ private void AddPropertyInfoFromPropertySchema(

case SchemaType.Integer:
comparisonKind = ComparisonKind.OperatorEquals;
hashKind = HashKind.ScalarValueType;
hashKind = scalarValueTypeHashKind;
initializationKind = InitializationKind.SimpleAssign;
type = MakeProperIntegerType(_generateJsonIntegerAs,
propertySchema.Minimum, propertySchema.ExclusiveMinimum,
propertySchema.Maximum, propertySchema.ExclusiveMaximum,
scalarValueTypeNullable,
out namespaceName);
break;

case SchemaType.String:
comparisonKind = ComparisonKind.OperatorEquals;
hashKind = HashKind.ScalarReferenceType;
Expand Down Expand Up @@ -445,8 +453,13 @@ private TypeSyntax MakeObjectTypeFromReference(UriOrFragment reference, out stri
return MakeNamedType(className, out namespaceName);
}

private TypeSyntax MakeNamedType(string typeName, out string namespaceName)
private TypeSyntax MakeNamedType(string typeName, out string namespaceName, bool nullable = false)
{
if (nullable)
{
typeName += "?";
}

string unqualifiedTypeName = GetUnqualifiedTypeName(typeName, out namespaceName);

return SyntaxFactory.ParseTypeName(unqualifiedTypeName);
Expand Down Expand Up @@ -630,7 +643,9 @@ private string GetUnqualifiedTypeName(string typeName, out string namespaceName)
namespaceName = null;
}

return unqualifiedTypeName.ToPascalCase();
namespaceName = namespaceName == "System" ? null : namespaceName;

@shaopeng-gh shaopeng-gh Oct 27, 2022

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

namespaceName = namespaceName == "System" ? null : namespaceName;

This is because using System; is not required.


return typeName.StartsWith("System.") ? unqualifiedTypeName : unqualifiedTypeName.ToPascalCase();

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unqualifiedTypeName

This is because we don't want to use Int, should be int.
All system types are already with correct case we don't need to manually do ToPascalCase.

}

private void OnAdditionalTypeRequired(CodeGenHint hint, JsonSchema schema)
Expand Down Expand Up @@ -687,20 +702,21 @@ private TypeSyntax GetConcreteType(string propertyName, string interfaceName)
internal TypeSyntax MakeProperIntegerType(GenerateJsonIntegerOption generateJsonIntegerAs,
double? minimum, bool? exclusiveMinimum,
double? maximum, bool? exclusiveMaximum,
bool nullable,
out string namespaceName)
{
namespaceName = null;

switch (generateJsonIntegerAs)
{
case GenerateJsonIntegerOption.Long:
return SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.LongKeyword));
return MakeNamedType(LongType, out namespaceName, nullable);
case GenerateJsonIntegerOption.BigInteger:
return MakeNamedType(BigIntegerType, out namespaceName);
return MakeNamedType(BigIntegerType, out namespaceName, nullable);
case GenerateJsonIntegerOption.Auto:
if (!minimum.HasValue || !maximum.HasValue)
{
return MakeNamedType(BigIntegerType, out namespaceName);
return MakeNamedType(BigIntegerType, out namespaceName, nullable);
}

if (exclusiveMinimum == true)
Expand All @@ -720,12 +736,12 @@ internal TypeSyntax MakeProperIntegerType(GenerateJsonIntegerOption generateJson

if (minimum.Value < int.MinValue || maximum.Value > int.MaxValue)
{
return SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.LongKeyword));
return MakeNamedType(LongType, out namespaceName, nullable);
}

return SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.IntKeyword));
return MakeNamedType(IntType, out namespaceName, nullable);
default:
return SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.IntKeyword));
return MakeNamedType(IntType, out namespaceName, nullable);
}
}

Expand Down
1 change: 1 addition & 0 deletions src/ReleaseHistory.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Microsoft Json Schema Packages

## **Unreleased**
* BREAKING: .NET type to express Json integers now will be nullable if the property is not required and also without default. [#167](https://github.com/microsoft/jschema/pull/167)
* FEATURE: Add new option for specifying .NET type to express Json numbers: `--generate-json-number-as = double | float | decimal` with a default of `double`. [#166](https://github.com/microsoft/jschema/pull/166)

## **2.1.0** [Pointer](https://www.nuget.org/packages/Microsoft.Json.Pointer/2.1.0) | [Schema](https://www.nuget.org/packages/Microsoft.Json.Schema/2.1.0)| [Schema.ToDotNet](https://www.nuget.org/packages/Microsoft.Json.Schema.ToDotNet/2.1.0)| [Schema.Validation](https://www.nuget.org/packages/Microsoft.Json.Schema.Validation/2.1.0)
Expand Down