From fef7259808162333730b8bfff5b1fff49be8693c Mon Sep 17 00:00:00 2001 From: Larry Golding Date: Thu, 27 Dec 2018 09:45:47 -0800 Subject: [PATCH 1/8] Introduce ClassGenerator.GenerateDefaultInitializations (empty). --- src/Json.Schema.ToDotNet/ClassGenerator.cs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/Json.Schema.ToDotNet/ClassGenerator.cs b/src/Json.Schema.ToDotNet/ClassGenerator.cs index 8d209405..de432179 100644 --- a/src/Json.Schema.ToDotNet/ClassGenerator.cs +++ b/src/Json.Schema.ToDotNet/ClassGenerator.cs @@ -372,7 +372,7 @@ private ConstructorDeclarationSyntax GenerateDefaultConstructor() { return SyntaxFactory.ConstructorDeclaration(SuffixedTypeName) .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword)) - .AddBodyStatements() + .AddBodyStatements(GenerateDefaultInitializations()) .WithLeadingTrivia( SyntaxHelper.MakeDocComment( string.Format( @@ -381,6 +381,24 @@ private ConstructorDeclarationSyntax GenerateDefaultConstructor() SuffixedTypeName))); } + /// + /// Generates an initialization statement for each property for which the + /// schema specifies a default value. + /// + /// + /// The resulting statements are inserted into the default constructor. + /// This ensures that the default values are set even if the object is + /// not the result of deserializing a JSON instance document. + /// + /// + /// An array containing one initialization statement for each property + /// for which the schema specifies a default value. + /// + private StatementSyntax[] GenerateDefaultInitializations() + { + return new StatementSyntax[0]; + } + private ConstructorDeclarationSyntax GeneratePropertyCtor() { // Generate the argument list that will be passed from the copy ctor to the From df1b3fe2f5eace41714d5630bd3ba3316b1dfbae Mon Sep 17 00:00:00 2001 From: Larry Golding Date: Thu, 27 Dec 2018 11:32:46 -0800 Subject: [PATCH 2/8] Add Default to PropertyInfo. --- src/Json.Schema.UnitTests/JsonSchemaTests.cs | 84 +++++++++++++++++++- src/Json.Schema/JsonSchema.cs | 30 +++++++ 2 files changed, 112 insertions(+), 2 deletions(-) diff --git a/src/Json.Schema.UnitTests/JsonSchemaTests.cs b/src/Json.Schema.UnitTests/JsonSchemaTests.cs index 8e389934..72cb33de 100644 --- a/src/Json.Schema.UnitTests/JsonSchemaTests.cs +++ b/src/Json.Schema.UnitTests/JsonSchemaTests.cs @@ -73,7 +73,8 @@ public class JsonSchemaTests ""uniqueItems"": true, ""format"": ""date-time"", ""maximimum"": 2, - ""exclusiveMaximum"": false + ""exclusiveMaximum"": false, + ""default"": 2, }", @"{ ""id"": ""http://x/y#"", @@ -128,7 +129,8 @@ public class JsonSchemaTests ""uniqueItems"": true, ""format"": ""date-time"", ""maximimum"": 2, - ""exclusiveMaximum"": false + ""exclusiveMaximum"": false, + ""default"": 2 }", true ), @@ -773,6 +775,84 @@ public class JsonSchemaTests }", false ), + + new EqualityTestCase( + "Same integer defaults", + @"{ + ""default"": 2 + }", + @"{ + ""default"": 2 + }", + true), + + new EqualityTestCase( + "Different integer defaults", + @"{ + ""default"": 2 + }", + @"{ + ""default"": 3 + }", + false), + + new EqualityTestCase( + "Same string defaults", + @"{ + ""default"": ""2"" + }", + @"{ + ""default"": ""2"" + }", + true), + + new EqualityTestCase( + "Different string defaults", + @"{ + ""default"": ""2"" + }", + @"{ + ""default"": ""3"" + }", + false), + + new EqualityTestCase( + "Same Boolean defaults", + @"{ + ""default"": true + }", + @"{ + ""default"": true + }", + true), + + new EqualityTestCase( + "Different Boolean defaults", + @"{ + ""default"": false + }", + @"{ + ""default"": true + }", + false), + + new EqualityTestCase( + "Different default types", + @"{ + ""default"": 2 + }", + @"{ + ""default"": ""2"" + }", + false), + + new EqualityTestCase( + "Present and missing defaults", + @"{ + ""default"": 2 + }", + @"{}", + false) }; [Theory(DisplayName = "JsonSchema equality")] diff --git a/src/Json.Schema/JsonSchema.cs b/src/Json.Schema/JsonSchema.cs index 392d9e55..22112e46 100644 --- a/src/Json.Schema/JsonSchema.cs +++ b/src/Json.Schema/JsonSchema.cs @@ -102,6 +102,7 @@ public JsonSchema(JsonSchema other) PatternProperties = new Dictionary(other.PatternProperties); } + Default = other.Default; Pattern = other.Pattern; MaxLength = other.MaxLength; MinLength = other.MinLength; @@ -266,6 +267,12 @@ public JsonSchema(JsonSchema other) /// public int? MinLength { get; set; } + /// + /// Gets or sets the value the property should be taken to have if it is absent + /// from an instance document. + /// + public object Default { get; set; } + /// /// Gets or sets a regular expression which a string schema instance must match. /// @@ -547,6 +554,7 @@ private static JsonSchema Collapse(JsonSchema schema, JsonSchema rootSchema) collapsedSchema.Items.SingleSchema = referencedSchema.Items.SingleSchema; } + collapsedSchema.Default = referencedSchema.Default; collapsedSchema.Pattern = referencedSchema.Pattern; collapsedSchema.MaxLength = referencedSchema.MaxLength; collapsedSchema.MinLength = referencedSchema.MinLength; @@ -583,6 +591,7 @@ public override int GetHashCode() Required, Definitions, Reference, + Default, Pattern, MaxLength, MinLength, @@ -646,6 +655,7 @@ public bool Equals(JsonSchema other) && (Reference == null ? other.Reference == null : Reference.Equals(other.Reference)) + && DefaultsAreEqual(Default, other.Default) && Pattern == other.Pattern && MaxLength == other.MaxLength && MinLength == other.MinLength @@ -691,5 +701,25 @@ public bool Equals(JsonSchema other) { return !(left == right); } + + private static bool DefaultsAreEqual(object left, object right) + { + if ((left == null) != (right == null)) { return false; } + + if (left == null) { return true; } + + Type leftType = left.GetType(), + rightType = right.GetType(); + if (leftType != rightType) { return false; } + + string leftString = left as String; + if (leftString != null) { return leftString.Equals(right as string, StringComparison.Ordinal); } + + if (left is bool) { return (bool)left == (bool)right; } + if (left is long) { return (long)left == (long)right; } + if (left is double) { return (double)left == (double)right; } + + return left == right; // Not good. How do we deal with other types? + } } } From 57472132f841b81f71daad7605f0d768f835bf71 Mon Sep 17 00:00:00 2001 From: Larry Golding Date: Thu, 27 Dec 2018 19:12:41 -0800 Subject: [PATCH 3/8] Emit default initializers for integer and string properties. --- .../DataModelGeneratorTests.cs | 40 ++++++++- src/Json.Schema.ToDotNet/ClassGenerator.cs | 81 ++++++++++++++++++- src/Json.Schema.ToDotNet/PropertyInfo.cs | 10 +++ .../PropertyInfoDictionary.cs | 5 +- 4 files changed, 128 insertions(+), 8 deletions(-) diff --git a/src/Json.Schema.ToDotNet.UnitTests/DataModelGeneratorTests.cs b/src/Json.Schema.ToDotNet.UnitTests/DataModelGeneratorTests.cs index 9a702e0b..fe3848c5 100644 --- a/src/Json.Schema.ToDotNet.UnitTests/DataModelGeneratorTests.cs +++ b/src/Json.Schema.ToDotNet.UnitTests/DataModelGeneratorTests.cs @@ -871,10 +871,20 @@ public void GeneratesCloningCode() ""type"": ""integer"", ""description"": ""An integer property."" }, + ""intPropWithDefault"": { + ""type"": ""integer"", + ""description"": ""An integer property with a default value."", + ""default"": 42 + }, ""stringProp"": { ""type"": ""string"", ""description"": ""A string property."" }, + ""stringPropWithDefault"": { + ""type"": ""string"", + ""description"": ""A string property with a default value."", + ""default"": ""42"" + }, ""arrayProp"": { ""type"": ""array"", ""description"": ""An array property."", @@ -1031,12 +1041,24 @@ public SNodeKind SNodeKind [DataMember(Name = ""intProp"", IsRequired = false, EmitDefaultValue = false)] public int IntProp { get; set; } + /// + /// An integer property with a default value. + /// + [DataMember(Name = ""intPropWithDefault"", IsRequired = false, EmitDefaultValue = false)] + public int IntPropWithDefault { get; set; } + /// /// A string property. /// [DataMember(Name = ""stringProp"", IsRequired = false, EmitDefaultValue = false)] public string StringProp { get; set; } + /// + /// A string property with a default value. + /// + [DataMember(Name = ""stringPropWithDefault"", IsRequired = false, EmitDefaultValue = false)] + public string StringPropWithDefault { get; set; } + /// /// An array property. /// @@ -1110,6 +1132,8 @@ public SNodeKind SNodeKind /// public C() { + IntPropWithDefault = 42; + StringPropWithDefault = ""42""; } /// @@ -1118,9 +1142,15 @@ public C() /// /// An initialization value for the property. /// + /// + /// An initialization value for the property. + /// /// /// An initialization value for the property. /// + /// + /// An initialization value for the property. + /// /// /// An initialization value for the property. /// @@ -1157,9 +1187,9 @@ public C() /// /// An initialization value for the property. /// - public C(int intProp, string stringProp, IEnumerable arrayProp, Uri uriProp, DateTime dateTimeProp, D referencedTypeProp, IEnumerable arrayOfRefProp, IEnumerable> arrayOfArrayProp, IDictionary dictionaryProp, IDictionary dictionaryWithPrimitiveSchemaProp, IDictionary dictionaryWithObjectSchemaProp, IDictionary> dictionaryWithObjectArraySchemaProp, IDictionary dictionaryWithUriKeyProp, IDictionary dictionaryWithHintedValueProp) + public C(int intProp, int intPropWithDefault, string stringProp, string stringPropWithDefault, IEnumerable arrayProp, Uri uriProp, DateTime dateTimeProp, D referencedTypeProp, IEnumerable arrayOfRefProp, IEnumerable> arrayOfArrayProp, IDictionary dictionaryProp, IDictionary dictionaryWithPrimitiveSchemaProp, IDictionary dictionaryWithObjectSchemaProp, IDictionary> dictionaryWithObjectArraySchemaProp, IDictionary dictionaryWithUriKeyProp, IDictionary dictionaryWithHintedValueProp) { - Init(intProp, stringProp, arrayProp, uriProp, dateTimeProp, referencedTypeProp, arrayOfRefProp, arrayOfArrayProp, dictionaryProp, dictionaryWithPrimitiveSchemaProp, dictionaryWithObjectSchemaProp, dictionaryWithObjectArraySchemaProp, dictionaryWithUriKeyProp, dictionaryWithHintedValueProp); + Init(intProp, intPropWithDefault, stringProp, stringPropWithDefault, arrayProp, uriProp, dateTimeProp, referencedTypeProp, arrayOfRefProp, arrayOfArrayProp, dictionaryProp, dictionaryWithPrimitiveSchemaProp, dictionaryWithObjectSchemaProp, dictionaryWithObjectArraySchemaProp, dictionaryWithUriKeyProp, dictionaryWithHintedValueProp); } /// @@ -1178,7 +1208,7 @@ public C(C other) throw new ArgumentNullException(nameof(other)); } - Init(other.IntProp, other.StringProp, other.ArrayProp, other.UriProp, other.DateTimeProp, other.ReferencedTypeProp, other.ArrayOfRefProp, other.ArrayOfArrayProp, other.DictionaryProp, other.DictionaryWithPrimitiveSchemaProp, other.DictionaryWithObjectSchemaProp, other.DictionaryWithObjectArraySchemaProp, other.DictionaryWithUriKeyProp, other.DictionaryWithHintedValueProp); + Init(other.IntProp, other.IntPropWithDefault, other.StringProp, other.StringPropWithDefault, other.ArrayProp, other.UriProp, other.DateTimeProp, other.ReferencedTypeProp, other.ArrayOfRefProp, other.ArrayOfArrayProp, other.DictionaryProp, other.DictionaryWithPrimitiveSchemaProp, other.DictionaryWithObjectSchemaProp, other.DictionaryWithObjectArraySchemaProp, other.DictionaryWithUriKeyProp, other.DictionaryWithHintedValueProp); } ISNode ISNode.DeepClone() @@ -1199,10 +1229,12 @@ private ISNode DeepCloneCore() return new C(this); } - private void Init(int intProp, string stringProp, IEnumerable arrayProp, Uri uriProp, DateTime dateTimeProp, D referencedTypeProp, IEnumerable arrayOfRefProp, IEnumerable> arrayOfArrayProp, IDictionary dictionaryProp, IDictionary dictionaryWithPrimitiveSchemaProp, IDictionary dictionaryWithObjectSchemaProp, IDictionary> dictionaryWithObjectArraySchemaProp, IDictionary dictionaryWithUriKeyProp, IDictionary dictionaryWithHintedValueProp) + private void Init(int intProp, int intPropWithDefault, string stringProp, string stringPropWithDefault, IEnumerable arrayProp, Uri uriProp, DateTime dateTimeProp, D referencedTypeProp, IEnumerable arrayOfRefProp, IEnumerable> arrayOfArrayProp, IDictionary dictionaryProp, IDictionary dictionaryWithPrimitiveSchemaProp, IDictionary dictionaryWithObjectSchemaProp, IDictionary> dictionaryWithObjectArraySchemaProp, IDictionary dictionaryWithUriKeyProp, IDictionary dictionaryWithHintedValueProp) { IntProp = intProp; + IntPropWithDefault = intPropWithDefault; StringProp = stringProp; + StringPropWithDefault = stringPropWithDefault; if (arrayProp != null) { var destination_0 = new List(); diff --git a/src/Json.Schema.ToDotNet/ClassGenerator.cs b/src/Json.Schema.ToDotNet/ClassGenerator.cs index de432179..14895f17 100644 --- a/src/Json.Schema.ToDotNet/ClassGenerator.cs +++ b/src/Json.Schema.ToDotNet/ClassGenerator.cs @@ -394,9 +394,86 @@ private ConstructorDeclarationSyntax GenerateDefaultConstructor() /// An array containing one initialization statement for each property /// for which the schema specifies a default value. /// - private StatementSyntax[] GenerateDefaultInitializations() + private ExpressionStatementSyntax[] GenerateDefaultInitializations() { - return new StatementSyntax[0]; + var initializations = new List(); + + foreach (string propertyName in PropInfoDictionary.GetPropertyNames()) + { + if (IncludeProperty(propertyName)) + { + PropertyInfo propInfo = PropInfoDictionary[propertyName]; + object defaultValue = propInfo.DefaultValue; + + ExpressionStatementSyntax initializationStatement = GenerateDefaultInitialization(propertyName, defaultValue); + if (initializationStatement != null) + { + initializations.Add(GenerateDefaultInitialization(propertyName, defaultValue)); + } + } + } + + return initializations.ToArray(); + } + + private ExpressionStatementSyntax GenerateDefaultInitialization( + string propertyName, + object defaultValue) + { + LiteralExpressionSyntax defaultValueExpression = GetLiteralExpressionForValue(defaultValue); + if (defaultValueExpression == null) { return null; } + + return SyntaxFactory.ExpressionStatement( + SyntaxFactory.AssignmentExpression( + SyntaxKind.SimpleAssignmentExpression, + SyntaxFactory.IdentifierName(propertyName), + defaultValueExpression)); + } + + private LiteralExpressionSyntax GetLiteralExpressionForValue(object value) + { + LiteralExpressionSyntax literalExpression = null; + + if (value is bool) + { + SyntaxKind literalSyntaxKind = (bool)value == true + ? SyntaxKind.TrueLiteralExpression + : SyntaxKind.FalseLiteralExpression; + + literalExpression = SyntaxFactory.LiteralExpression(literalSyntaxKind); + } + else if (value is int) + { + literalExpression = SyntaxFactory.LiteralExpression( + SyntaxKind.NumericLiteralExpression, + SyntaxFactory.Literal((int)value)); + } + else if (value is long) + { + literalExpression = SyntaxFactory.LiteralExpression( + SyntaxKind.NumericLiteralExpression, + SyntaxFactory.Literal((int)(long)value)); + } + else if (value is float) + { + literalExpression = SyntaxFactory.LiteralExpression( + SyntaxKind.NumericLiteralExpression, + SyntaxFactory.Literal((float)value)); + } + else if (value is double) + { + literalExpression = SyntaxFactory.LiteralExpression( + SyntaxKind.NumericLiteralExpression, + SyntaxFactory.Literal((double)value)); + } + else if (value is string) + { + literalExpression = SyntaxFactory.LiteralExpression( + SyntaxKind.StringLiteralExpression, + SyntaxFactory.Literal((string)value)); + } + + return literalExpression; } private ConstructorDeclarationSyntax GeneratePropertyCtor() diff --git a/src/Json.Schema.ToDotNet/PropertyInfo.cs b/src/Json.Schema.ToDotNet/PropertyInfo.cs index 1ab0e617..e67ccb07 100644 --- a/src/Json.Schema.ToDotNet/PropertyInfo.cs +++ b/src/Json.Schema.ToDotNet/PropertyInfo.cs @@ -42,6 +42,9 @@ public class PropertyInfo /// true if this property is required by the schema; /// otherwise false. /// + /// + /// The default value, if any, specified by the schema; otherwise null. + /// /// /// true if this property is of a type defined by the schema (or an; /// array of a schema-defined type otherwise false. @@ -61,6 +64,7 @@ public PropertyInfo( TypeSyntax type, string namespaceName, bool isRequired, + object defaultValue, bool isOfSchemaDefinedType, int arrayRank, int declarationOrder) @@ -74,6 +78,7 @@ public PropertyInfo( TypeName = type.ToString(); NamespaceName = namespaceName; IsRequired = isRequired; + DefaultValue = defaultValue; IsOfSchemaDefinedType = isOfSchemaDefinedType; ArrayRank = arrayRank; DeclarationOrder = declarationOrder; @@ -131,6 +136,11 @@ public PropertyInfo( /// public bool IsRequired { get; } + /// + /// Gets this property's default value, if the schema specifies one; otherwise null. + /// + public object DefaultValue; + /// /// Gets a value indicating whether this property is of a type defined by the schema. /// diff --git a/src/Json.Schema.ToDotNet/PropertyInfoDictionary.cs b/src/Json.Schema.ToDotNet/PropertyInfoDictionary.cs index aa98e83f..eca97da0 100644 --- a/src/Json.Schema.ToDotNet/PropertyInfoDictionary.cs +++ b/src/Json.Schema.ToDotNet/PropertyInfoDictionary.cs @@ -10,7 +10,6 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.Json.Schema.ToDotNet.Hints; -using Newtonsoft.Json.Linq; namespace Microsoft.Json.Schema.ToDotNet { @@ -284,7 +283,7 @@ private void AddPropertyInfoFromPropertySchema( OnAdditionalTypeRequired(enumHint, propertySchema); } else - { + { SchemaType propertyType = propertySchema.SafeGetType(); switch (propertyType) @@ -384,6 +383,7 @@ private void AddPropertyInfoFromPropertySchema( type, namespaceName, isRequired, + propertySchema.Default, isOfSchemaDefinedType, arrayRank, entries.Count))); @@ -477,6 +477,7 @@ private TypeSyntax MakeDictionaryType( type: valueType, namespaceName: dictionaryHint.NamespaceName, isRequired: true, + defaultValue: null, isOfSchemaDefinedType: false, arrayRank: 0, declarationOrder: 0))); From 9259ce59181706f0af68cc5f5dfd0e452e83d4c4 Mon Sep 17 00:00:00 2001 From: Larry Golding Date: Fri, 28 Dec 2018 10:10:19 -0800 Subject: [PATCH 4/8] Emit default initializers for Boolean properties. --- .../DataModelGeneratorTests.cs | 54 +++++++++++++++++-- 1 file changed, 50 insertions(+), 4 deletions(-) diff --git a/src/Json.Schema.ToDotNet.UnitTests/DataModelGeneratorTests.cs b/src/Json.Schema.ToDotNet.UnitTests/DataModelGeneratorTests.cs index fe3848c5..185563ca 100644 --- a/src/Json.Schema.ToDotNet.UnitTests/DataModelGeneratorTests.cs +++ b/src/Json.Schema.ToDotNet.UnitTests/DataModelGeneratorTests.cs @@ -885,6 +885,20 @@ public void GeneratesCloningCode() ""description"": ""A string property with a default value."", ""default"": ""42"" }, + ""boolProp"": { + ""type"": ""boolean"", + ""description"": ""A Boolean property."" + }, + ""boolPropWithTrueDefault"": { + ""type"": ""boolean"", + ""description"": ""A Boolean property with a true default value."", + ""default"": true + }, + ""boolPropWithFalseDefault"": { + ""type"": ""boolean"", + ""description"": ""A Boolean property with a false default value."", + ""default"": false + }, ""arrayProp"": { ""type"": ""array"", ""description"": ""An array property."", @@ -1059,6 +1073,24 @@ public SNodeKind SNodeKind [DataMember(Name = ""stringPropWithDefault"", IsRequired = false, EmitDefaultValue = false)] public string StringPropWithDefault { get; set; } + /// + /// A Boolean property. + /// + [DataMember(Name = ""boolProp"", IsRequired = false, EmitDefaultValue = false)] + public bool BoolProp { get; set; } + + /// + /// A Boolean property with a true default value. + /// + [DataMember(Name = ""boolPropWithTrueDefault"", IsRequired = false, EmitDefaultValue = false)] + public bool BoolPropWithTrueDefault { get; set; } + + /// + /// A Boolean property with a false default value. + /// + [DataMember(Name = ""boolPropWithFalseDefault"", IsRequired = false, EmitDefaultValue = false)] + public bool BoolPropWithFalseDefault { get; set; } + /// /// An array property. /// @@ -1134,6 +1166,8 @@ public C() { IntPropWithDefault = 42; StringPropWithDefault = ""42""; + BoolPropWithTrueDefault = true; + BoolPropWithFalseDefault = false; } /// @@ -1151,6 +1185,15 @@ public C() /// /// An initialization value for the property. /// + /// + /// An initialization value for the property. + /// + /// + /// An initialization value for the property. + /// + /// + /// An initialization value for the property. + /// /// /// An initialization value for the property. /// @@ -1187,9 +1230,9 @@ public C() /// /// An initialization value for the property. /// - public C(int intProp, int intPropWithDefault, string stringProp, string stringPropWithDefault, IEnumerable arrayProp, Uri uriProp, DateTime dateTimeProp, D referencedTypeProp, IEnumerable arrayOfRefProp, IEnumerable> arrayOfArrayProp, IDictionary dictionaryProp, IDictionary dictionaryWithPrimitiveSchemaProp, IDictionary dictionaryWithObjectSchemaProp, IDictionary> dictionaryWithObjectArraySchemaProp, IDictionary dictionaryWithUriKeyProp, IDictionary dictionaryWithHintedValueProp) + public C(int intProp, int intPropWithDefault, string stringProp, string stringPropWithDefault, bool boolProp, bool boolPropWithTrueDefault, bool boolPropWithFalseDefault, IEnumerable arrayProp, Uri uriProp, DateTime dateTimeProp, D referencedTypeProp, IEnumerable arrayOfRefProp, IEnumerable> arrayOfArrayProp, IDictionary dictionaryProp, IDictionary dictionaryWithPrimitiveSchemaProp, IDictionary dictionaryWithObjectSchemaProp, IDictionary> dictionaryWithObjectArraySchemaProp, IDictionary dictionaryWithUriKeyProp, IDictionary dictionaryWithHintedValueProp) { - Init(intProp, intPropWithDefault, stringProp, stringPropWithDefault, arrayProp, uriProp, dateTimeProp, referencedTypeProp, arrayOfRefProp, arrayOfArrayProp, dictionaryProp, dictionaryWithPrimitiveSchemaProp, dictionaryWithObjectSchemaProp, dictionaryWithObjectArraySchemaProp, dictionaryWithUriKeyProp, dictionaryWithHintedValueProp); + Init(intProp, intPropWithDefault, stringProp, stringPropWithDefault, boolProp, boolPropWithTrueDefault, boolPropWithFalseDefault, arrayProp, uriProp, dateTimeProp, referencedTypeProp, arrayOfRefProp, arrayOfArrayProp, dictionaryProp, dictionaryWithPrimitiveSchemaProp, dictionaryWithObjectSchemaProp, dictionaryWithObjectArraySchemaProp, dictionaryWithUriKeyProp, dictionaryWithHintedValueProp); } /// @@ -1208,7 +1251,7 @@ public C(C other) throw new ArgumentNullException(nameof(other)); } - Init(other.IntProp, other.IntPropWithDefault, other.StringProp, other.StringPropWithDefault, other.ArrayProp, other.UriProp, other.DateTimeProp, other.ReferencedTypeProp, other.ArrayOfRefProp, other.ArrayOfArrayProp, other.DictionaryProp, other.DictionaryWithPrimitiveSchemaProp, other.DictionaryWithObjectSchemaProp, other.DictionaryWithObjectArraySchemaProp, other.DictionaryWithUriKeyProp, other.DictionaryWithHintedValueProp); + Init(other.IntProp, other.IntPropWithDefault, other.StringProp, other.StringPropWithDefault, other.BoolProp, other.BoolPropWithTrueDefault, other.BoolPropWithFalseDefault, other.ArrayProp, other.UriProp, other.DateTimeProp, other.ReferencedTypeProp, other.ArrayOfRefProp, other.ArrayOfArrayProp, other.DictionaryProp, other.DictionaryWithPrimitiveSchemaProp, other.DictionaryWithObjectSchemaProp, other.DictionaryWithObjectArraySchemaProp, other.DictionaryWithUriKeyProp, other.DictionaryWithHintedValueProp); } ISNode ISNode.DeepClone() @@ -1229,12 +1272,15 @@ private ISNode DeepCloneCore() return new C(this); } - private void Init(int intProp, int intPropWithDefault, string stringProp, string stringPropWithDefault, IEnumerable arrayProp, Uri uriProp, DateTime dateTimeProp, D referencedTypeProp, IEnumerable arrayOfRefProp, IEnumerable> arrayOfArrayProp, IDictionary dictionaryProp, IDictionary dictionaryWithPrimitiveSchemaProp, IDictionary dictionaryWithObjectSchemaProp, IDictionary> dictionaryWithObjectArraySchemaProp, IDictionary dictionaryWithUriKeyProp, IDictionary dictionaryWithHintedValueProp) + private void Init(int intProp, int intPropWithDefault, string stringProp, string stringPropWithDefault, bool boolProp, bool boolPropWithTrueDefault, bool boolPropWithFalseDefault, IEnumerable arrayProp, Uri uriProp, DateTime dateTimeProp, D referencedTypeProp, IEnumerable arrayOfRefProp, IEnumerable> arrayOfArrayProp, IDictionary dictionaryProp, IDictionary dictionaryWithPrimitiveSchemaProp, IDictionary dictionaryWithObjectSchemaProp, IDictionary> dictionaryWithObjectArraySchemaProp, IDictionary dictionaryWithUriKeyProp, IDictionary dictionaryWithHintedValueProp) { IntProp = intProp; IntPropWithDefault = intPropWithDefault; StringProp = stringProp; StringPropWithDefault = stringPropWithDefault; + BoolProp = boolProp; + BoolPropWithTrueDefault = boolPropWithTrueDefault; + BoolPropWithFalseDefault = boolPropWithFalseDefault; if (arrayProp != null) { var destination_0 = new List(); From 2b8cd14f97f8730726d6ca4652a541c191a030f8 Mon Sep 17 00:00:00 2001 From: Larry Golding Date: Fri, 28 Dec 2018 11:16:18 -0800 Subject: [PATCH 5/8] Add initializers for number properties. --- .../DataModelGeneratorTests.cs | 38 +++++++++++++++++-- src/Json.Schema.ToDotNet/ClassGenerator.cs | 18 +++------ 2 files changed, 40 insertions(+), 16 deletions(-) diff --git a/src/Json.Schema.ToDotNet.UnitTests/DataModelGeneratorTests.cs b/src/Json.Schema.ToDotNet.UnitTests/DataModelGeneratorTests.cs index 185563ca..f250c35f 100644 --- a/src/Json.Schema.ToDotNet.UnitTests/DataModelGeneratorTests.cs +++ b/src/Json.Schema.ToDotNet.UnitTests/DataModelGeneratorTests.cs @@ -876,6 +876,15 @@ public void GeneratesCloningCode() ""description"": ""An integer property with a default value."", ""default"": 42 }, + ""numberProp"": { + ""type"": ""number"", + ""description"": ""A number property."" + }, + ""numberPropWithDefault"": { + ""type"": ""number"", + ""description"": ""A number property with a default value."", + ""default"": 42.1 + }, ""stringProp"": { ""type"": ""string"", ""description"": ""A string property."" @@ -1061,6 +1070,18 @@ public SNodeKind SNodeKind [DataMember(Name = ""intPropWithDefault"", IsRequired = false, EmitDefaultValue = false)] public int IntPropWithDefault { get; set; } + /// + /// A number property. + /// + [DataMember(Name = ""numberProp"", IsRequired = false, EmitDefaultValue = false)] + public double NumberProp { get; set; } + + /// + /// A number property with a default value. + /// + [DataMember(Name = ""numberPropWithDefault"", IsRequired = false, EmitDefaultValue = false)] + public double NumberPropWithDefault { get; set; } + /// /// A string property. /// @@ -1165,6 +1186,7 @@ public SNodeKind SNodeKind public C() { IntPropWithDefault = 42; + NumberPropWithDefault = 42.1; StringPropWithDefault = ""42""; BoolPropWithTrueDefault = true; BoolPropWithFalseDefault = false; @@ -1179,6 +1201,12 @@ public C() /// /// An initialization value for the property. /// + /// + /// An initialization value for the property. + /// + /// + /// An initialization value for the property. + /// /// /// An initialization value for the property. /// @@ -1230,9 +1258,9 @@ public C() /// /// An initialization value for the property. /// - public C(int intProp, int intPropWithDefault, string stringProp, string stringPropWithDefault, bool boolProp, bool boolPropWithTrueDefault, bool boolPropWithFalseDefault, IEnumerable arrayProp, Uri uriProp, DateTime dateTimeProp, D referencedTypeProp, IEnumerable arrayOfRefProp, IEnumerable> arrayOfArrayProp, IDictionary dictionaryProp, IDictionary dictionaryWithPrimitiveSchemaProp, IDictionary dictionaryWithObjectSchemaProp, IDictionary> dictionaryWithObjectArraySchemaProp, IDictionary dictionaryWithUriKeyProp, IDictionary dictionaryWithHintedValueProp) + public C(int intProp, int intPropWithDefault, double numberProp, double numberPropWithDefault, string stringProp, string stringPropWithDefault, bool boolProp, bool boolPropWithTrueDefault, bool boolPropWithFalseDefault, IEnumerable arrayProp, Uri uriProp, DateTime dateTimeProp, D referencedTypeProp, IEnumerable arrayOfRefProp, IEnumerable> arrayOfArrayProp, IDictionary dictionaryProp, IDictionary dictionaryWithPrimitiveSchemaProp, IDictionary dictionaryWithObjectSchemaProp, IDictionary> dictionaryWithObjectArraySchemaProp, IDictionary dictionaryWithUriKeyProp, IDictionary dictionaryWithHintedValueProp) { - Init(intProp, intPropWithDefault, stringProp, stringPropWithDefault, boolProp, boolPropWithTrueDefault, boolPropWithFalseDefault, arrayProp, uriProp, dateTimeProp, referencedTypeProp, arrayOfRefProp, arrayOfArrayProp, dictionaryProp, dictionaryWithPrimitiveSchemaProp, dictionaryWithObjectSchemaProp, dictionaryWithObjectArraySchemaProp, dictionaryWithUriKeyProp, dictionaryWithHintedValueProp); + Init(intProp, intPropWithDefault, numberProp, numberPropWithDefault, stringProp, stringPropWithDefault, boolProp, boolPropWithTrueDefault, boolPropWithFalseDefault, arrayProp, uriProp, dateTimeProp, referencedTypeProp, arrayOfRefProp, arrayOfArrayProp, dictionaryProp, dictionaryWithPrimitiveSchemaProp, dictionaryWithObjectSchemaProp, dictionaryWithObjectArraySchemaProp, dictionaryWithUriKeyProp, dictionaryWithHintedValueProp); } /// @@ -1251,7 +1279,7 @@ public C(C other) throw new ArgumentNullException(nameof(other)); } - Init(other.IntProp, other.IntPropWithDefault, other.StringProp, other.StringPropWithDefault, other.BoolProp, other.BoolPropWithTrueDefault, other.BoolPropWithFalseDefault, other.ArrayProp, other.UriProp, other.DateTimeProp, other.ReferencedTypeProp, other.ArrayOfRefProp, other.ArrayOfArrayProp, other.DictionaryProp, other.DictionaryWithPrimitiveSchemaProp, other.DictionaryWithObjectSchemaProp, other.DictionaryWithObjectArraySchemaProp, other.DictionaryWithUriKeyProp, other.DictionaryWithHintedValueProp); + Init(other.IntProp, other.IntPropWithDefault, other.NumberProp, other.NumberPropWithDefault, other.StringProp, other.StringPropWithDefault, other.BoolProp, other.BoolPropWithTrueDefault, other.BoolPropWithFalseDefault, other.ArrayProp, other.UriProp, other.DateTimeProp, other.ReferencedTypeProp, other.ArrayOfRefProp, other.ArrayOfArrayProp, other.DictionaryProp, other.DictionaryWithPrimitiveSchemaProp, other.DictionaryWithObjectSchemaProp, other.DictionaryWithObjectArraySchemaProp, other.DictionaryWithUriKeyProp, other.DictionaryWithHintedValueProp); } ISNode ISNode.DeepClone() @@ -1272,10 +1300,12 @@ private ISNode DeepCloneCore() return new C(this); } - private void Init(int intProp, int intPropWithDefault, string stringProp, string stringPropWithDefault, bool boolProp, bool boolPropWithTrueDefault, bool boolPropWithFalseDefault, IEnumerable arrayProp, Uri uriProp, DateTime dateTimeProp, D referencedTypeProp, IEnumerable arrayOfRefProp, IEnumerable> arrayOfArrayProp, IDictionary dictionaryProp, IDictionary dictionaryWithPrimitiveSchemaProp, IDictionary dictionaryWithObjectSchemaProp, IDictionary> dictionaryWithObjectArraySchemaProp, IDictionary dictionaryWithUriKeyProp, IDictionary dictionaryWithHintedValueProp) + private void Init(int intProp, int intPropWithDefault, double numberProp, double numberPropWithDefault, string stringProp, string stringPropWithDefault, bool boolProp, bool boolPropWithTrueDefault, bool boolPropWithFalseDefault, IEnumerable arrayProp, Uri uriProp, DateTime dateTimeProp, D referencedTypeProp, IEnumerable arrayOfRefProp, IEnumerable> arrayOfArrayProp, IDictionary dictionaryProp, IDictionary dictionaryWithPrimitiveSchemaProp, IDictionary dictionaryWithObjectSchemaProp, IDictionary> dictionaryWithObjectArraySchemaProp, IDictionary dictionaryWithUriKeyProp, IDictionary dictionaryWithHintedValueProp) { IntProp = intProp; IntPropWithDefault = intPropWithDefault; + NumberProp = numberProp; + NumberPropWithDefault = numberPropWithDefault; StringProp = stringProp; StringPropWithDefault = stringPropWithDefault; BoolProp = boolProp; diff --git a/src/Json.Schema.ToDotNet/ClassGenerator.cs b/src/Json.Schema.ToDotNet/ClassGenerator.cs index 14895f17..f3525585 100644 --- a/src/Json.Schema.ToDotNet/ClassGenerator.cs +++ b/src/Json.Schema.ToDotNet/ClassGenerator.cs @@ -442,23 +442,17 @@ private LiteralExpressionSyntax GetLiteralExpressionForValue(object value) literalExpression = SyntaxFactory.LiteralExpression(literalSyntaxKind); } - else if (value is int) - { - literalExpression = SyntaxFactory.LiteralExpression( - SyntaxKind.NumericLiteralExpression, - SyntaxFactory.Literal((int)value)); - } else if (value is long) { literalExpression = SyntaxFactory.LiteralExpression( SyntaxKind.NumericLiteralExpression, SyntaxFactory.Literal((int)(long)value)); - } - else if (value is float) - { - literalExpression = SyntaxFactory.LiteralExpression( - SyntaxKind.NumericLiteralExpression, - SyntaxFactory.Literal((float)value)); + // Note: the extra cast compensates for a mismatch between our code generation + // and Newtonsoft.Json's deserialization behavior. Newtonsoft deserializes + // integer properties as Int64 (long), but we generate integer properties + // with type int. The extra cast causes Roslyn to emit the literal 42, + // which can be assigned to an int, rather than 42L, which cannot. We should + // consider changing the code generation to emit longs for integer properties. } else if (value is double) { From 6ef6d4d657d54a31049b9639c033d5f9598046d7 Mon Sep 17 00:00:00 2001 From: Larry Golding Date: Fri, 28 Dec 2018 12:01:17 -0800 Subject: [PATCH 6/8] Emit System.ComponentModel.DefaultValue when needed. --- .../DataModelGeneratorTests.cs | 50 +++++++++++-------- src/Json.Schema.ToDotNet/ClassGenerator.cs | 23 ++++++++- .../ClassOrInterfaceGenerator.cs | 4 +- .../InterfaceGenerator.cs | 2 +- .../Json.Schema.ToDotNet.csproj | 15 ++++++ .../PropertyInfoDictionary.cs | 16 +++--- .../Resources.Designer.cs | 4 +- src/Json.Schema.ToDotNet/Resources.resx | 2 +- src/Json.Schema.ToDotNet/TypeGenerator.cs | 10 ++-- 9 files changed, 82 insertions(+), 44 deletions(-) diff --git a/src/Json.Schema.ToDotNet.UnitTests/DataModelGeneratorTests.cs b/src/Json.Schema.ToDotNet.UnitTests/DataModelGeneratorTests.cs index f250c35f..b7c4104c 100644 --- a/src/Json.Schema.ToDotNet.UnitTests/DataModelGeneratorTests.cs +++ b/src/Json.Schema.ToDotNet.UnitTests/DataModelGeneratorTests.cs @@ -1039,6 +1039,7 @@ public void GeneratesCloningCode() @"using System; using System.CodeDom.Compiler; using System.Collections.Generic; +using System.ComponentModel; using System.Runtime.Serialization; namespace N @@ -1068,6 +1069,7 @@ public SNodeKind SNodeKind /// An integer property with a default value. /// [DataMember(Name = ""intPropWithDefault"", IsRequired = false, EmitDefaultValue = false)] + [DefaultValue(42)] public int IntPropWithDefault { get; set; } /// @@ -1080,6 +1082,7 @@ public SNodeKind SNodeKind /// A number property with a default value. /// [DataMember(Name = ""numberPropWithDefault"", IsRequired = false, EmitDefaultValue = false)] + [DefaultValue(42.1)] public double NumberPropWithDefault { get; set; } /// @@ -1092,6 +1095,7 @@ public SNodeKind SNodeKind /// A string property with a default value. /// [DataMember(Name = ""stringPropWithDefault"", IsRequired = false, EmitDefaultValue = false)] + [DefaultValue(""42"")] public string StringPropWithDefault { get; set; } /// @@ -1104,12 +1108,14 @@ public SNodeKind SNodeKind /// A Boolean property with a true default value. /// [DataMember(Name = ""boolPropWithTrueDefault"", IsRequired = false, EmitDefaultValue = false)] + [DefaultValue(true)] public bool BoolPropWithTrueDefault { get; set; } /// /// A Boolean property with a false default value. /// [DataMember(Name = ""boolPropWithFalseDefault"", IsRequired = false, EmitDefaultValue = false)] + [DefaultValue(false)] public bool BoolPropWithFalseDefault { get; set; } /// @@ -1196,67 +1202,67 @@ public C() /// Initializes a new instance of the class from the supplied values. /// /// - /// An initialization value for the property. + /// An initialization value for the property. /// /// - /// An initialization value for the property. + /// An initialization value for the property. /// /// - /// An initialization value for the property. + /// An initialization value for the property. /// /// - /// An initialization value for the property. + /// An initialization value for the property. /// /// - /// An initialization value for the property. + /// An initialization value for the property. /// /// - /// An initialization value for the property. + /// An initialization value for the property. /// /// - /// An initialization value for the property. + /// An initialization value for the property. /// /// - /// An initialization value for the property. + /// An initialization value for the property. /// /// - /// An initialization value for the property. + /// An initialization value for the property. /// /// - /// An initialization value for the property. + /// An initialization value for the property. /// /// - /// An initialization value for the property. + /// An initialization value for the property. /// /// - /// An initialization value for the property. + /// An initialization value for the property. /// /// - /// An initialization value for the property. + /// An initialization value for the property. /// /// - /// An initialization value for the property. + /// An initialization value for the property. /// /// - /// An initialization value for the property. + /// An initialization value for the property. /// /// - /// An initialization value for the property. + /// An initialization value for the property. /// /// - /// An initialization value for the property. + /// An initialization value for the property. /// /// - /// An initialization value for the property. + /// An initialization value for the property. /// /// - /// An initialization value for the property. + /// An initialization value for the property. /// /// - /// An initialization value for the property. + /// An initialization value for the property. /// /// - /// An initialization value for the property. + /// An initialization value for the property. /// public C(int intProp, int intPropWithDefault, double numberProp, double numberPropWithDefault, string stringProp, string stringPropWithDefault, bool boolProp, bool boolPropWithTrueDefault, bool boolPropWithFalseDefault, IEnumerable arrayProp, Uri uriProp, DateTime dateTimeProp, D referencedTypeProp, IEnumerable arrayOfRefProp, IEnumerable> arrayOfArrayProp, IDictionary dictionaryProp, IDictionary dictionaryWithPrimitiveSchemaProp, IDictionary dictionaryWithObjectSchemaProp, IDictionary> dictionaryWithObjectArraySchemaProp, IDictionary dictionaryWithUriKeyProp, IDictionary dictionaryWithHintedValueProp) { @@ -2558,7 +2564,7 @@ public C() /// Initializes a new instance of the class from the supplied values. /// /// - /// An initialization value for the property. + /// An initialization value for the property. /// public C(IEnumerable uriFormattedStrings) { diff --git a/src/Json.Schema.ToDotNet/ClassGenerator.cs b/src/Json.Schema.ToDotNet/ClassGenerator.cs index f3525585..25e5aaaa 100644 --- a/src/Json.Schema.ToDotNet/ClassGenerator.cs +++ b/src/Json.Schema.ToDotNet/ClassGenerator.cs @@ -27,6 +27,9 @@ public class ClassGenerator : ClassOrInterfaceGenerator // Name used for the parameters of the copy ctor. private const string OtherParameterName = "other"; + private const string DefaultValueAttributeNamespaceName = "System.ComponentModel"; + private const string DefaultValueAttributeName = "DefaultValue"; + private const string DataContractAttributeName = "DataContract"; private const string DataMemberAttributeName = "DataMember"; private const string DataMemberNamePropertyName = "Name"; @@ -241,7 +244,7 @@ private MemberDeclarationSyntax GenerateValueGetHashCodeMethod() .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)); } - protected override AttributeSyntax[] GeneratePropertyAttributes(string propertyName, string serializedName, bool isRequired) + protected override AttributeSyntax[] GeneratePropertyAttributes(string propertyName, string serializedName, bool isRequired, object defaultValue) { var attributes = new List(); @@ -279,6 +282,24 @@ protected override AttributeSyntax[] GeneratePropertyAttributes(string propertyN attributes.Add(dataMemberAttribute); + if (defaultValue != null) + { + AddUsing(DefaultValueAttributeNamespaceName); + + var defaultValueArguments = new List + { + SyntaxFactory.AttributeArgument(GetLiteralExpressionForValue(defaultValue)) + }; + + AttributeSyntax defaultValueAttribute = + SyntaxFactory.Attribute( + SyntaxFactory.IdentifierName(DefaultValueAttributeName), + SyntaxFactory.AttributeArgumentList( + SyntaxFactory.SeparatedList(defaultValueArguments))); + + attributes.Add(defaultValueAttribute); + } + string hintDictionaryKey = MakeHintDictionaryKey(propertyName); AttributeHint[] attributeHints = HintDictionary?.GetHints(hintDictionaryKey); if (attributeHints != null) diff --git a/src/Json.Schema.ToDotNet/ClassOrInterfaceGenerator.cs b/src/Json.Schema.ToDotNet/ClassOrInterfaceGenerator.cs index 43733be4..5312eee7 100644 --- a/src/Json.Schema.ToDotNet/ClassOrInterfaceGenerator.cs +++ b/src/Json.Schema.ToDotNet/ClassOrInterfaceGenerator.cs @@ -25,7 +25,7 @@ public ClassOrInterfaceGenerator( PropInfoDictionary = propertyInfoDictionary; } - protected abstract AttributeSyntax[] GeneratePropertyAttributes(string propertyName, string serializedName, bool isRequired); + protected abstract AttributeSyntax[] GeneratePropertyAttributes(string propertyName, string serializedName, bool isRequired, object defaultValue); protected abstract SyntaxToken[] GeneratePropertyModifiers(string propertyName); @@ -103,7 +103,7 @@ private PropertyDeclarationSyntax CreatePropertyDeclaration(string propertyName) .AddModifiers(GeneratePropertyModifiers(propertyName)) .AddAccessorListAccessors(GeneratePropertyAccessors()); - AttributeSyntax[] attributes = GeneratePropertyAttributes(propertyName, info.SerializedName, info.IsRequired); + AttributeSyntax[] attributes = GeneratePropertyAttributes(propertyName, info.SerializedName, info.IsRequired, info.DefaultValue); if (attributes.Length > 0) { propDecl = propDecl.AddAttributeLists(attributes diff --git a/src/Json.Schema.ToDotNet/InterfaceGenerator.cs b/src/Json.Schema.ToDotNet/InterfaceGenerator.cs index 589ffbf4..7a3e5814 100644 --- a/src/Json.Schema.ToDotNet/InterfaceGenerator.cs +++ b/src/Json.Schema.ToDotNet/InterfaceGenerator.cs @@ -41,7 +41,7 @@ public override void AddMembers() .AddMembers(GenerateProperties()); } - protected override AttributeSyntax[] GeneratePropertyAttributes(string propertyName, string serializedName, bool isRequired) + protected override AttributeSyntax[] GeneratePropertyAttributes(string propertyName, string serializedName, bool isRequired, object defaultValue) { return new AttributeSyntax[0]; } diff --git a/src/Json.Schema.ToDotNet/Json.Schema.ToDotNet.csproj b/src/Json.Schema.ToDotNet/Json.Schema.ToDotNet.csproj index 0ff38bee..8d6dc6b8 100644 --- a/src/Json.Schema.ToDotNet/Json.Schema.ToDotNet.csproj +++ b/src/Json.Schema.ToDotNet/Json.Schema.ToDotNet.csproj @@ -16,6 +16,21 @@ + + + True + True + Resources.resx + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + + + diff --git a/src/Json.Schema.ToDotNet/PropertyInfoDictionary.cs b/src/Json.Schema.ToDotNet/PropertyInfoDictionary.cs index eca97da0..cf74dcf5 100644 --- a/src/Json.Schema.ToDotNet/PropertyInfoDictionary.cs +++ b/src/Json.Schema.ToDotNet/PropertyInfoDictionary.cs @@ -56,8 +56,8 @@ public class PropertyInfoDictionary : IReadOnlyDictionary /// public delegate void AdditionalTypeRequiredDelegate(AdditionalTypeRequiredInfo additionalTypeRequiredInfo); - private AdditionalTypeRequiredDelegate _additionalTypeRequiredDelegate; - private string _typeNameSuffix; + private readonly AdditionalTypeRequiredDelegate _additionalTypeRequiredDelegate; + private readonly string _typeNameSuffix; /// /// Initializes a new instance of the class. @@ -142,8 +142,7 @@ public static string MakeDictionaryItemKeyName(string propertyName) public static SyntaxKind GetTypeKeywordFromSchemaType(SchemaType type) { - SyntaxKind typeKeyword; - if (!s_SchemaTypeToSyntaxKindDictionary.TryGetValue(type, out typeKeyword)) + if (!s_SchemaTypeToSyntaxKindDictionary.TryGetValue(type, out SyntaxKind typeKeyword)) { typeKeyword = SyntaxKind.ObjectKeyword; } @@ -157,8 +156,7 @@ public PropertyInfo this[string key] { get { - PropertyInfo info; - if (!TryGetValue(key, out info)) + if (!TryGetValue(key, out PropertyInfo info)) { throw new ApplicationException($"The schema does not contain information describing the property or element {key}."); } @@ -228,8 +226,6 @@ private void AddPropertyInfoFromPropertySchema( string referencedEnumTypeName; bool isOfSchemaDefinedType = false; int arrayRank = 0; - EnumHint enumHint; - DictionaryHint dictionaryHint; if (propertySchema.IsDateTime()) { @@ -245,7 +241,7 @@ private void AddPropertyInfoFromPropertySchema( initializationKind = InitializationKind.Uri; type = MakeNamedType("System.Uri", out namespaceName); } - else if (propertySchema.ShouldBeDictionary(_typeName, schemaPropertyName, _hintDictionary, out dictionaryHint)) + else if (propertySchema.ShouldBeDictionary(_typeName, schemaPropertyName, _hintDictionary, out DictionaryHint dictionaryHint)) { comparisonKind = ComparisonKind.Dictionary; hashKind = HashKind.Dictionary; @@ -270,7 +266,7 @@ private void AddPropertyInfoFromPropertySchema( initializationKind = InitializationKind.SimpleAssign; type = MakeNamedType(referencedEnumTypeName, out namespaceName); } - else if (propertySchema.ShouldBeEnum(_typeName, schemaPropertyName, _hintDictionary, out enumHint)) + else if (propertySchema.ShouldBeEnum(_typeName, schemaPropertyName, _hintDictionary, out EnumHint enumHint)) { comparisonKind = ComparisonKind.OperatorEquals; hashKind = HashKind.ScalarValueType; diff --git a/src/Json.Schema.ToDotNet/Resources.Designer.cs b/src/Json.Schema.ToDotNet/Resources.Designer.cs index f1f0643c..01bc443b 100644 --- a/src/Json.Schema.ToDotNet/Resources.Designer.cs +++ b/src/Json.Schema.ToDotNet/Resources.Designer.cs @@ -19,7 +19,7 @@ namespace Microsoft.Json.Schema.ToDotNet { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Resources { @@ -214,7 +214,7 @@ internal static string KindEnumNoneDescription { } /// - /// Looks up a localized string similar to An initialization value for the <see cref="P: {0}" /> property.. + /// Looks up a localized string similar to An initialization value for the <see cref="P:{0}" /> property.. /// internal static string PropertyCtorParamDescription { get { diff --git a/src/Json.Schema.ToDotNet/Resources.resx b/src/Json.Schema.ToDotNet/Resources.resx index 44f5acbe..fffa6fac 100644 --- a/src/Json.Schema.ToDotNet/Resources.resx +++ b/src/Json.Schema.ToDotNet/Resources.resx @@ -169,7 +169,7 @@ An uninitialized kind. - An initialization value for the <see cref="P: {0}" /> property. + An initialization value for the <see cref="P:{0}" /> property. Initializes a new instance of the <see cref="{0}" /> class from the supplied values. diff --git a/src/Json.Schema.ToDotNet/TypeGenerator.cs b/src/Json.Schema.ToDotNet/TypeGenerator.cs index af93ece6..4ce16d56 100644 --- a/src/Json.Schema.ToDotNet/TypeGenerator.cs +++ b/src/Json.Schema.ToDotNet/TypeGenerator.cs @@ -9,7 +9,7 @@ namespace Microsoft.Json.Schema.ToDotNet { public abstract class TypeGenerator { - private string _typeNameSuffix; + private readonly string _typeNameSuffix; protected TypeGenerator( JsonSchema schema, @@ -73,12 +73,12 @@ public string Generate(string namespaceName, string typeName, string copyrightNo protected void AddUsing(string namespaceName) { - if (Usings == null) + Usings = Usings ?? new List(); + + if (!Usings.Contains(namespaceName)) { - Usings = new List(); + Usings.Add(namespaceName); } - - Usings.Add(namespaceName); } } } From 38d8c45316fe4e4acfb1b51142ff5f5248a82321 Mon Sep 17 00:00:00 2001 From: Larry Golding Date: Fri, 28 Dec 2018 13:22:28 -0800 Subject: [PATCH 7/8] Remove unnecessary test. --- src/Json.Schema.ToDotNet/TypeGenerator.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Json.Schema.ToDotNet/TypeGenerator.cs b/src/Json.Schema.ToDotNet/TypeGenerator.cs index 4ce16d56..f1336710 100644 --- a/src/Json.Schema.ToDotNet/TypeGenerator.cs +++ b/src/Json.Schema.ToDotNet/TypeGenerator.cs @@ -75,10 +75,7 @@ protected void AddUsing(string namespaceName) { Usings = Usings ?? new List(); - if (!Usings.Contains(namespaceName)) - { - Usings.Add(namespaceName); - } + Usings.Add(namespaceName); } } } From bf9479d0f7db21976e9938c9956e005262eec183 Mon Sep 17 00:00:00 2001 From: Larry Golding Date: Fri, 28 Dec 2018 14:39:18 -0800 Subject: [PATCH 8/8] Address feedback from @michaelcfanning. --- src/Json.Schema.ToDotNet/ClassGenerator.cs | 2 +- .../EqualityComparerGenerator.cs | 10 +++++-- .../RewritingVisitorGenerator.cs | 6 ++-- .../SyntaxNodeExtensions.cs | 8 ++--- src/Json.Schema.ToDotNet/TypeGenerator.cs | 4 +-- src/Json.Schema/JsonSchema.cs | 29 +++---------------- 6 files changed, 18 insertions(+), 41 deletions(-) diff --git a/src/Json.Schema.ToDotNet/ClassGenerator.cs b/src/Json.Schema.ToDotNet/ClassGenerator.cs index 25e5aaaa..e67590f3 100644 --- a/src/Json.Schema.ToDotNet/ClassGenerator.cs +++ b/src/Json.Schema.ToDotNet/ClassGenerator.cs @@ -429,7 +429,7 @@ private ExpressionStatementSyntax[] GenerateDefaultInitializations() ExpressionStatementSyntax initializationStatement = GenerateDefaultInitialization(propertyName, defaultValue); if (initializationStatement != null) { - initializations.Add(GenerateDefaultInitialization(propertyName, defaultValue)); + initializations.Add(initializationStatement); } } } diff --git a/src/Json.Schema.ToDotNet/EqualityComparerGenerator.cs b/src/Json.Schema.ToDotNet/EqualityComparerGenerator.cs index 08253b43..842cb174 100644 --- a/src/Json.Schema.ToDotNet/EqualityComparerGenerator.cs +++ b/src/Json.Schema.ToDotNet/EqualityComparerGenerator.cs @@ -111,16 +111,20 @@ internal string Generate(string className, PropertyInfoDictionary propertyInfoDi GenerateEqualsMethod(), GenerateGetHashCodeMethod()); - var usings = new List + var usings = new HashSet { "System", // For Object. "System.Collections.Generic" // For IEqualityComparer }; - usings.AddRange(_propertyInfoDictionary + IEnumerable namespaceNames = _propertyInfoDictionary .Values .Select(propertyInfo => propertyInfo.NamespaceName) - .Where(namespaceName => !string.IsNullOrWhiteSpace(namespaceName))); + .Where(namespaceName => !string.IsNullOrWhiteSpace(namespaceName)); + foreach (string namespaceName in namespaceNames) + { + usings.Add(namespaceName); + } return classDeclaration.Format( _copyrightNotice, diff --git a/src/Json.Schema.ToDotNet/RewritingVisitorGenerator.cs b/src/Json.Schema.ToDotNet/RewritingVisitorGenerator.cs index 99b73d2e..3eb94e56 100644 --- a/src/Json.Schema.ToDotNet/RewritingVisitorGenerator.cs +++ b/src/Json.Schema.ToDotNet/RewritingVisitorGenerator.cs @@ -79,7 +79,7 @@ internal string GenerateRewritingVisitor() .AddMembers( GenerateVisitClassMethods()); - var usings = new List { "System", "System.Collections.Generic", "System.Linq" }; + var usings = new HashSet { "System", "System.Collections.Generic", "System.Linq" }; string summaryComment = string.Format( CultureInfo.CurrentCulture, @@ -356,9 +356,7 @@ private StatementSyntax[] GeneratePropertyVisits(string className) continue; } - int arrayRank = 0; - bool isDictionary = false; - string propertyName = propertyNameWithRank.BasePropertyName(out arrayRank, out isDictionary); + string propertyName = propertyNameWithRank.BasePropertyName(out int arrayRank, out bool isDictionary); TypeSyntax collectionType = propertyInfoDictionary.GetConcreteListType(propertyName); TypeSyntax elementType = propertyInfoDictionary[propertyNameWithRank].Type; diff --git a/src/Json.Schema.ToDotNet/SyntaxNodeExtensions.cs b/src/Json.Schema.ToDotNet/SyntaxNodeExtensions.cs index 2176810d..f46481d6 100644 --- a/src/Json.Schema.ToDotNet/SyntaxNodeExtensions.cs +++ b/src/Json.Schema.ToDotNet/SyntaxNodeExtensions.cs @@ -54,7 +54,7 @@ internal static class SyntaxNodeExtensions internal static string Format( this BaseTypeDeclarationSyntax typeDecl, string copyrightNotice, - List usings, + HashSet usings, string namespaceName, string summaryComment) { @@ -74,15 +74,11 @@ internal static string Format( CompilationUnitSyntax compilationUnit = SyntaxFactory.CompilationUnit() .AddMembers(namespaceDecl); - if (usings == null) - { - usings = new List(); - } + usings = usings ?? new HashSet(); usings.Add("System.CodeDom.Compiler"); // For GeneratedCodeAttribute UsingDirectiveSyntax[] usingDirectives = usings - .Distinct() .OrderBy(u => u, UsingComparer.Instance) .Select(u => SyntaxFactory.UsingDirective(MakeQualifiedName(u))) .ToArray(); diff --git a/src/Json.Schema.ToDotNet/TypeGenerator.cs b/src/Json.Schema.ToDotNet/TypeGenerator.cs index f1336710..4522aff8 100644 --- a/src/Json.Schema.ToDotNet/TypeGenerator.cs +++ b/src/Json.Schema.ToDotNet/TypeGenerator.cs @@ -37,7 +37,7 @@ public string SuffixedTypeName /// protected BaseTypeDeclarationSyntax TypeDeclaration { get; set; } - protected List Usings { get; private set; } + protected HashSet Usings { get; private set; } public abstract BaseTypeDeclarationSyntax GenerateTypeDeclaration(); @@ -73,7 +73,7 @@ public string Generate(string namespaceName, string typeName, string copyrightNo protected void AddUsing(string namespaceName) { - Usings = Usings ?? new List(); + Usings = Usings ?? new HashSet(); Usings.Add(namespaceName); } diff --git a/src/Json.Schema/JsonSchema.cs b/src/Json.Schema/JsonSchema.cs index 22112e46..1a47aa5b 100644 --- a/src/Json.Schema/JsonSchema.cs +++ b/src/Json.Schema/JsonSchema.cs @@ -527,8 +527,7 @@ private static JsonSchema Collapse(JsonSchema schema, JsonSchema rootSchema) string definitionName = schema.Reference.GetDefinitionName(); - JsonSchema referencedSchema; - if (rootSchema.Definitions == null || !rootSchema.Definitions.TryGetValue(definitionName, out referencedSchema)) + if (rootSchema.Definitions == null || !rootSchema.Definitions.TryGetValue(definitionName, out JsonSchema referencedSchema)) { throw Error.CreateException( Resources.ErrorDefinitionDoesNotExist, @@ -619,7 +618,7 @@ public override int GetHashCode() public bool Equals(JsonSchema other) { - if ((object)other == null) + if (other is null) { return false; } @@ -655,7 +654,7 @@ public bool Equals(JsonSchema other) && (Reference == null ? other.Reference == null : Reference.Equals(other.Reference)) - && DefaultsAreEqual(Default, other.Default) + && Object.Equals(Default, other.Default) && Pattern == other.Pattern && MaxLength == other.MaxLength && MinLength == other.MinLength @@ -689,7 +688,7 @@ public bool Equals(JsonSchema other) return true; } - if ((object)left == null) + if (left is null) { return false; } @@ -701,25 +700,5 @@ public bool Equals(JsonSchema other) { return !(left == right); } - - private static bool DefaultsAreEqual(object left, object right) - { - if ((left == null) != (right == null)) { return false; } - - if (left == null) { return true; } - - Type leftType = left.GetType(), - rightType = right.GetType(); - if (leftType != rightType) { return false; } - - string leftString = left as String; - if (leftString != null) { return leftString.Equals(right as string, StringComparison.Ordinal); } - - if (left is bool) { return (bool)left == (bool)right; } - if (left is long) { return (long)left == (long)right; } - if (left is double) { return (double)left == (double)right; } - - return left == right; // Not good. How do we deal with other types? - } } }