Skip to content

Commit

Permalink
Merge pull request #121 from hadashiA/ku/naming-convention-options
Browse files Browse the repository at this point in the history
Support `NamingConvention` option for per serialize/deserialize
  • Loading branch information
hadashiA authored Oct 6, 2024
2 parents 0109adf + 6191324 commit 0bf70aa
Show file tree
Hide file tree
Showing 44 changed files with 1,762 additions and 253 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ on:
push:
branches:
- "master"
pull_request:
pull_request_target:
branches:
- "master"

Expand Down
38 changes: 26 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,31 @@ documents[2]["Fatal"] // #=> "Unknown variable \"bar\""

:exclamation: By default, VYaml maps C# property names in lower camel case (e.g. `propertyName`) format to yaml keys.

If you want to customize this behaviour, use argment of `[YamlObject]` attribute.
If you want to customize this behaviour, `YamlSerializerOptions.NamingConvention` to set it.

```cs
var options = YamlSerializerOptions.Standard;
options.NamingConvention = NamingConvention.SnakeCase;
YamlSerializer.Serialize(new A { FooBar = 123 }, options); // #=> "{ foo_bar: 123 }"
```

List of possible values:
- NamingConvention.LowerCamelCase
- Like `propertyName`
- NamingConvention.UpperCamelCase:
- Like `PropertyName`
- NamingConvention.SnakeCase:
- Like `property_name`
- NamingConvention.KebabCase:
- Like `property-name`


> [!TIP]
> If you specify an option other than the default `LowerCamelCase`, there will be a slight performance degradation at runtime.

You may specify NamingConvention for each type declaration by `[YamlObject]` attribute.
In this case, no performance degradation occurs.

```csharp
[YamlObject(NamingConvention.SnakeCase)]
Expand All @@ -253,17 +277,7 @@ This serialize as:
foo_bar: 100
```

List of possible values:
- NamingConvention.LowerCamelCase
- Like `propertyName`
- NamingConvention.UpperCamelCase:
- Like `PropertyName`
- NamingConvention.SnakeCase:
- Like `property_name`
- NamingConvention.KebabCase:
- Like `property-name`

Alos, you can change the key name each members with `[YamlMember("name")]`
Also, you can change the key name each members with `[YamlMember("name")]`

```csharp
[YamlObject]
Expand Down
1 change: 0 additions & 1 deletion VYaml.Annotations/Attributes.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#nullable enable
using System;

namespace VYaml.Annotations
Expand Down
2 changes: 1 addition & 1 deletion VYaml.Annotations/VYaml.Annotations.csproj
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.1;net6.0;net8.0</TargetFrameworks>
<TargetFrameworks>netstandard2.0;netstandard2.1;net6.0;net8.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>9</LangVersion>
Expand Down
10 changes: 10 additions & 0 deletions VYaml.SourceGenerator.Roslyn3/VYaml.SourceGenerator.Roslyn3.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,16 @@
Exclude="**/obj/**;**/VYamlIncrementalSourceGenerator.cs;**/Shims/**" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\VYaml.Annotations\VYaml.Annotations.csproj" />
</ItemGroup>

<ItemGroup>
<Compile Include="..\VYaml\Serialization\NamingConventionMutator.cs">
<Link>NamingConventionMutator.cs</Link>
</Compile>
</ItemGroup>

<ItemGroup>
<!-- Mainly used for Unity, Unity 2021.3 has Roslyn 3.9.0(see: Editor\Data\DotNetSdkRoslyn\ -->
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.9.0" PrivateAssets="all" />
Expand Down
1 change: 1 addition & 0 deletions VYaml.SourceGenerator/CodeWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -117,5 +117,6 @@ public void EndBlock()
public void Clear()
{
buffer.Clear();
indentLevel = 0;
}
}
25 changes: 20 additions & 5 deletions VYaml.SourceGenerator/Emitter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -273,13 +273,21 @@ static bool TryEmitSerializeMethod(TypeMeta typeMeta, CodeWriter codeWriter, in
codeWriter.AppendLine("emitter.BeginMapping();");
foreach (var memberMeta in memberMetas)
{
if (memberMeta.HasKeyNameAlias)
if (memberMeta.HasKeyNameAlias || typeMeta.NamingConventionByType != NamingConvention.LowerCamelCase)
{
codeWriter.AppendLine($"emitter.WriteString(\"{memberMeta.KeyName}\");");
}
else
{
codeWriter.AppendLine($"emitter.WriteString(\"{memberMeta.KeyName}\", ScalarStyle.Plain);");
using (codeWriter.BeginBlockScope($"if (context.Options.NamingConvention == global::VYaml.Annotations.NamingConvention.{memberMeta.NamingConventionByType})"))
{
codeWriter.AppendLine($"emitter.WriteScalar({memberMeta.Name}KeyUtf8Bytes);");
}
using (codeWriter.BeginBlockScope("else"))
{
codeWriter.AppendLine($"global::VYaml.Serialization.NamingConventionMutator.MutateToThreadStaticBufferUtf8({memberMeta.Name}KeyUtf8Bytes, context.Options.NamingConvention, out var mutated, out var written);");
codeWriter.AppendLine("emitter.WriteScalar(mutated.AsSpan(0, written));");
}
}
codeWriter.AppendLine($"context.Serialize(ref emitter, value.{memberMeta.Name});");
}
Expand Down Expand Up @@ -402,6 +410,13 @@ static bool TryEmitDeserializeMethod(
codeWriter.AppendLine("throw new YamlSerializerException(parser.CurrentMark, \"Custom type deserialization supports only string key\");");
}
codeWriter.AppendLine();

using (codeWriter.BeginBlockScope($"if (context.Options.NamingConvention != global::VYaml.Annotations.NamingConvention.{typeMeta.NamingConventionByType})"))
{
codeWriter.AppendLine($"global::VYaml.Serialization.NamingConventionMutator.MutateToThreadStaticBufferUtf8(key, global::VYaml.Annotations.NamingConvention.{typeMeta.NamingConventionByType}, out var mutated, out var written);");
codeWriter.AppendLine("key = mutated.AsSpan(0, written);");
}

using (codeWriter.BeginBlockScope("switch (key.Length)"))
{
var membersByNameLength = typeMeta.MemberMetas.GroupBy(x => x.KeyNameUtf8Bytes.Length);
Expand All @@ -415,8 +430,8 @@ static bool TryEmitDeserializeMethod(
using (codeWriter.BeginBlockScope($"{branching} (key.SequenceEqual({memberMeta.Name}KeyUtf8Bytes))"))
{
codeWriter.AppendLine("parser.Read(); // skip key");
codeWriter.AppendLine(
$"__{memberMeta.Name}__ = context.DeserializeWithAlias<{memberMeta.FullTypeName}>(ref parser);");
codeWriter.AppendLine($"__{memberMeta.Name}__ = context.DeserializeWithAlias<{memberMeta.FullTypeName}>(ref parser);");
codeWriter.AppendLine("continue;");
}
branching = "else if";
}
Expand Down Expand Up @@ -582,4 +597,4 @@ static bool TryGetConstructor(
constructedMembers = parameterMembers;
return !error;
}
}
}
75 changes: 0 additions & 75 deletions VYaml.SourceGenerator/KeyNameMutator.cs

This file was deleted.

10 changes: 6 additions & 4 deletions VYaml.SourceGenerator/MemberMeta.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class MemberMeta
public bool HasExplicitOrder { get; }
public bool HasKeyNameAlias { get; }
public string KeyName { get; }
public NamingConvention NamingConventionByType { get; }

public bool IsConstructorParameter { get; set; }
public bool HasExplicitDefaultValueFromConstructor { get; set; }
Expand All @@ -26,12 +27,13 @@ class MemberMeta
public byte[] KeyNameUtf8Bytes => keyNameUtf8Bytes ??= System.Text.Encoding.UTF8.GetBytes(KeyName);
byte[]? keyNameUtf8Bytes;

public MemberMeta(ISymbol symbol, ReferenceSymbols references, NamingConvention namingConvention, int sequentialOrder)
public MemberMeta(ISymbol symbol, ReferenceSymbols references, int sequentialOrder, NamingConvention namingConventionByType = default)
{
Symbol = symbol;
Name = symbol.Name;
Order = sequentialOrder;
KeyName = KeyNameMutator.Mutate(Name, namingConvention);
NamingConventionByType = namingConventionByType;
KeyName = NamingConventionMutator.Mutate(Name, NamingConventionByType);

var memberAttribute = symbol.GetAttribute(references.YamlMemberAttribute);
if (memberAttribute != null)
Expand Down Expand Up @@ -68,7 +70,7 @@ public MemberMeta(ISymbol symbol, ReferenceSymbols references, NamingConvention
}
else
{
throw new Exception("member is not field or property.");
throw new InvalidOperationException("member is not field or property.");
}
FullTypeName = MemberType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
}
Expand All @@ -83,7 +85,7 @@ public string EmitDefaultValue()
{
if (!HasExplicitDefaultValueFromConstructor)
{
return (MemberType is { IsReferenceType: true, NullableAnnotation: NullableAnnotation.Annotated })
return (MemberType is { IsReferenceType: true, NullableAnnotation: NullableAnnotation.Annotated or NullableAnnotation.None })
? $"default({FullTypeName})!"
: $"default({FullTypeName})";
}
Expand Down
Loading

0 comments on commit 0bf70aa

Please sign in to comment.