Skip to content

Commit

Permalink
Improve property name escaping
Browse files Browse the repository at this point in the history
* don't allow "init", "fromJS" or "toJSON" for TypeScript member name
* improve performance by not doing string.Replace unless necessary
* update xUnit
  • Loading branch information
lahma committed Oct 25, 2023
1 parent 85fdd00 commit 91ccd4b
Show file tree
Hide file tree
Showing 16 changed files with 176 additions and 58 deletions.
6 changes: 3 additions & 3 deletions src/NJsonSchema.Benchmark/NJsonSchema.Benchmark.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@

<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.13.7" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
<PackageReference Include="NBench" Version="2.0.1" />
<PackageReference Include="Pro.NBench.xUnit" Version="2.0.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" PrivateAssets="all" />
<PackageReference Include="xunit" Version="2.5.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3" PrivateAssets="all" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public async Task When_empty_class_inherits_from_dictionary_then_allOf_inheritan
//// Assert
var dictionarySchema = schema.Definitions["EmptyClassInheritingDictionary"];

Assert.Equal(0, dictionarySchema.AllOf.Count);
Assert.Empty(dictionarySchema.AllOf);
Assert.True(dictionarySchema.IsDictionary);
Assert.Contains("Foobar.", data);
Assert.Contains("Foobar.", code);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" PrivateAssets="all" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
<PackageReference Include="xunit" Version="2.5.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.11.0" Condition="'$(TargetFramework)' == 'net6.0'" />
<PackageReference Condition="'$(TargetFramework)' == 'net6.0'" Include="System.ComponentModel.Annotations" Version="4.4.0" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,21 @@
namespace NJsonSchema.CodeGeneration.CSharp
{
/// <summary>Generates the property name for a given CSharp <see cref="JsonSchemaProperty"/>.</summary>
public class CSharpPropertyNameGenerator : IPropertyNameGenerator
public sealed class CSharpPropertyNameGenerator : IPropertyNameGenerator
{
private static readonly char[] _reservedFirstPassChars = { '"', '\'', '@', '?', '!', '$', '[', ']', '(', ')', '.', '=', '+' };
private static readonly char[] _reservedSecondPassChars = { '*', ':', '-', '#', '&' };

/// <summary>Generates the property name.</summary>
/// <param name="property">The property.</param>
/// <returns>The new name.</returns>
public virtual string Generate(JsonSchemaProperty property)
public string Generate(JsonSchemaProperty property)
{
return ConversionUtilities.ConvertToUpperCamelCase(property.Name
.Replace("\"", string.Empty)
var name = property.Name;

if (name.IndexOfAny(_reservedFirstPassChars) != -1)
{
name = name.Replace("\"", string.Empty)
.Replace("'", string.Empty)
.Replace("@", string.Empty)
.Replace("?", string.Empty)
Expand All @@ -29,12 +35,22 @@ public virtual string Generate(JsonSchemaProperty property)
.Replace(")", string.Empty)
.Replace(".", "-")
.Replace("=", "-")
.Replace("+", "plus"), true)
.Replace("*", "Star")
.Replace(":", "_")
.Replace("-", "_")
.Replace("#", "_")
.Replace("&", "And");
.Replace("+", "plus");
}

name = ConversionUtilities.ConvertToUpperCamelCase(name, true);

if (name.IndexOfAny(_reservedSecondPassChars) != -1)
{
name = name
.Replace("*", "Star")
.Replace(":", "_")
.Replace("-", "_")
.Replace("#", "_")
.Replace("&", "And");
}

return name;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ public async Task When_dates_are_converted_then_JsonInheritanceConverter_should_
}

[Fact]
public void JsonInheritanceConverter_is_thread_safe()
public async Task JsonInheritanceConverter_is_thread_safe()
{
//// Arrange
var tasks = new List<Task>();
Expand All @@ -196,7 +196,7 @@ public void JsonInheritanceConverter_is_thread_safe()
}

//// Act
Task.WaitAll(tasks.ToArray());
await Task.WhenAll(tasks.ToArray());

//// Assert
// No exceptions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,12 @@
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="1.3.2" Condition="'$(TargetFramework)' == 'net462'" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.11.0" Condition="'$(TargetFramework)' == 'net6.0'" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
<PackageReference Include="xunit" Version="2.5.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
Expand Down
4 changes: 2 additions & 2 deletions src/NJsonSchema.CodeGeneration.Tests/Samples/SampleTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ public async Task When_JSON_contains_DateTime_is_available_then_string_validator
var errors = schema.Validate(dataJson);

//// Assert
Assert.Equal(0, errors.Count);
Assert.Empty(errors);
}

[Fact]
Expand Down Expand Up @@ -135,7 +135,7 @@ public async Task When_JSON_contains_DateTime_is_available_then_JObject_validato
var errors = schema.Validate(data);

//// Assert
Assert.Equal(0, errors.Count);
Assert.Empty(errors);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public async Task When_empty_class_inherits_from_dictionary_then_allOf_inheritan
//// Assert
var dschema = schema.Definitions["EmptyClassInheritingDictionary"];

Assert.Equal(0, dschema.AllOf.Count);
Assert.Empty(dschema.AllOf);
Assert.True(dschema.IsDictionary);

if (inlineNamedDictionaries)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.2" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.0.1" PrivateAssets="all" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
<PackageReference Include="Verify.XUnit" Version="14.7.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" PrivateAssets="all" />
<PackageReference Include="xunit" Version="2.5.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3" PrivateAssets="all" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using System.Threading.Tasks;
using NJsonSchema.Annotations;
using NJsonSchema.NewtonsoftJson.Generation;
using VerifyXunit;
using Xunit;

using static NJsonSchema.CodeGeneration.TypeScript.Tests.VerifyHelper;

namespace NJsonSchema.CodeGeneration.TypeScript.Tests;

[UsesVerify]
public class PropertyNameTests
{
private class TypeWithRestrictedProperties
{
public string Constructor { get; set; }
public string Init { get; set; }
public string FromJS { get; set; }
public string ToJSON { get; set; }
}

[Fact]
public async Task When_class_has_restricted_properties_they_are_escaped()
{
var schema = NewtonsoftJsonSchemaGenerator.FromType<TypeWithRestrictedProperties>();

var generator = new TypeScriptGenerator(schema, new TypeScriptGeneratorSettings { TypeScriptVersion = 4.3m });
var output = generator.GenerateFile(nameof(TypeWithRestrictedProperties));

await Verify(output);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
//----------------------
// <auto-generated>
// </auto-generated>
//----------------------







export class TypeWithRestrictedProperties implements ITypeWithRestrictedProperties {
constructor_!: string | undefined;
init_!: string | undefined;
fromJS_!: string | undefined;
toJSON_!: string | undefined;

constructor(data?: ITypeWithRestrictedProperties) {
if (data) {
for (var property in data) {
if (data.hasOwnProperty(property))
(<any>this)[property] = (<any>data)[property];
}
}
}

init(_data?: any) {
if (_data) {
this.constructor_ = _data["Constructor"];
this.init_ = _data["Init"];
this.fromJS_ = _data["FromJS"];
this.toJSON_ = _data["ToJSON"];
}
}

static fromJS(data: any): TypeWithRestrictedProperties {
data = typeof data === 'object' ? data : {};
let result = new TypeWithRestrictedProperties();
result.init(data);
return result;
}

toJSON(data?: any) {
data = typeof data === 'object' ? data : {};
data["Constructor"] = this.constructor_;
data["Init"] = this.init_;
data["FromJS"] = this.fromJS_;
data["ToJSON"] = this.toJSON_;
return data;
}
}

export interface ITypeWithRestrictedProperties {
constructor_: string | undefined;
init_: string | undefined;
fromJS_: string | undefined;
toJSON_: string | undefined;
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,32 +6,43 @@
// <author>Rico Suter, mail@rsuter.com</author>
//-----------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Linq;

namespace NJsonSchema.CodeGeneration.TypeScript
{
/// <summary>Generates the property name for a given TypeScript <see cref="JsonSchemaProperty"/>.</summary>
public class TypeScriptPropertyNameGenerator : IPropertyNameGenerator
public sealed class TypeScriptPropertyNameGenerator : IPropertyNameGenerator
{
private static readonly char[] _reservedFirstPassChars = { '"', '@', '?', '.', '=', '+' };
private static readonly char[] _reservedSecondPassChars = { '*', ':', '-' };

/// <summary>Gets or sets the reserved names.</summary>
public IEnumerable<string> ReservedPropertyNames { get; set; } = new List<string> { "constructor" };
public HashSet<string> ReservedPropertyNames { get; set; } = new(StringComparer.Ordinal) { "constructor", "init", "fromJS", "toJSON" };

/// <summary>Generates the property name.</summary>
/// <param name="property">The property.</param>
/// <returns>The new name.</returns>
public virtual string Generate(JsonSchemaProperty property)
/// <inheritdoc />
public string Generate(JsonSchemaProperty property)
{
var name = ConversionUtilities.ConvertToLowerCamelCase(property.Name
.Replace("\"", string.Empty)
var name = property.Name;

if (name.IndexOfAny(_reservedFirstPassChars) != -1)
{
name = name.Replace("\"", string.Empty)
.Replace("@", string.Empty)
.Replace("?", string.Empty)
.Replace(".", "-")
.Replace("=", "-")
.Replace("+", "plus"), true)
.Replace("*", "Star")
.Replace(":", "_")
.Replace("-", "_");
.Replace("+", "plus");
}

name = ConversionUtilities.ConvertToLowerCamelCase(name, true);

if (name.IndexOfAny(_reservedSecondPassChars) != -1)
{
name = name.Replace("*", "Star")
.Replace(":", "_")
.Replace("-", "_");
}

if (ReservedPropertyNames.Contains(name))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" PrivateAssets="all" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
<PackageReference Include="xunit" Version="2.5.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3" PrivateAssets="all" />
<PackageReference Include="NodaTime" Version="3.1.9" />
<Reference Condition="'$(TargetFramework)' == 'net462'" Include="System.ComponentModel.DataAnnotations"></Reference>
<DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" />
Expand Down
6 changes: 3 additions & 3 deletions src/NJsonSchema.Tests/NJsonSchema.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" PrivateAssets="all" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
<PackageReference Include="xunit" Version="2.5.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3" PrivateAssets="all" />
<PackageReference Include="NodaTime" Version="3.1.9" />
<Reference Condition="'$(TargetFramework)' == 'net462'" Include="System.ComponentModel.DataAnnotations"></Reference>
<DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" />
Expand Down
6 changes: 3 additions & 3 deletions src/NJsonSchema.Yaml.Tests/NJsonSchema.Yaml.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
<PackageReference Include="NSwag.Core.Yaml" Version="14.0.0-preview003" />
<PackageReference Include="xunit" Version="2.5.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.0" PrivateAssets="all" />
<PackageReference Include="xunit" Version="2.5.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3" PrivateAssets="all" />
<DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" />
<PackageReference Include="NodaTime" Version="3.1.9" />
</ItemGroup>
Expand Down
Loading

0 comments on commit 91ccd4b

Please sign in to comment.