This is an automated email from the ASF dual-hosted git repository.
chaokunyang pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/fory.git
The following commit(s) were added to refs/heads/main by this push:
new 98e20c397 refactor(csharp): split Fory object attributes (#3709)
98e20c397 is described below
commit 98e20c397770e7551be6d239c2343b440312db2d
Author: Shawn Yang <[email protected]>
AuthorDate: Wed May 27 15:22:40 2026 +0800
refactor(csharp): split Fory object attributes (#3709)
## Why?
## What does this PR do?
## Related issues
## AI Contribution Checklist
- [ ] Substantial AI assistance was used in this PR: `yes` / `no`
- [ ] If `yes`, I included a completed [AI Contribution
Checklist](https://github.com/apache/fory/blob/main/AI_POLICY.md#9-contributor-checklist-for-ai-assisted-prs)
in this PR description and the required `AI Usage Disclosure`.
- [ ] If `yes`, my PR description includes the required `ai_review`
summary and screenshot evidence of the final clean AI review results
from both fresh reviewers on the current PR diff or current HEAD after
the latest code changes.
## Does this PR introduce any user-facing change?
- [ ] Does this PR introduce any public API change?
- [ ] Does this PR introduce any binary protocol compatibility change?
## Benchmark
---
README.md | 2 +-
benchmarks/csharp/BenchmarkModels.cs | 20 +--
compiler/README.md | 4 +-
compiler/fory_compiler/generators/csharp.py | 7 +-
.../fory_compiler/tests/test_csharp_generator.py | 26 ++++
csharp/README.md | 18 +--
.../Fory.Generator/AnalyzerReleases.Unshipped.md | 3 +-
...oryObjectGenerator.cs => ForyModelGenerator.cs} | 138 +++++++++++++++++++--
csharp/src/Fory/Attributes.cs | 22 +++-
csharp/src/Fory/TypeInfo.cs | 2 +-
csharp/tests/Fory.Tests/ForyGeneratorTests.cs | 57 ++++++++-
csharp/tests/Fory.Tests/ForyRuntimeTests.cs | 76 ++++++------
csharp/tests/Fory.Tests/RuntimeEdgeCaseTests.cs | 8 +-
csharp/tests/Fory.XlangPeer/Program.cs | 86 ++++++-------
docs/compiler/generated-code.md | 7 +-
docs/compiler/index.md | 4 +-
docs/compiler/schema-idl.md | 4 +-
docs/guide/csharp/basic-serialization.md | 8 +-
docs/guide/csharp/custom-serializers.md | 4 +-
docs/guide/csharp/index.md | 6 +-
docs/guide/csharp/references.md | 2 +-
docs/guide/csharp/schema-evolution.md | 4 +-
docs/guide/csharp/schema-metadata.md | 8 +-
docs/guide/csharp/supported-types.md | 2 +-
docs/guide/csharp/xlang-serialization.md | 2 +-
docs/guide/dart/xlang-serialization.md | 2 +-
docs/guide/go/type-registration.md | 4 +-
docs/guide/python/xlang-serialization.md | 4 +-
docs/specification/xlang_type_mapping.md | 18 +--
29 files changed, 379 insertions(+), 169 deletions(-)
diff --git a/README.md b/README.md
index 507891cad..729382467 100644
--- a/README.md
+++ b/README.md
@@ -472,7 +472,7 @@ console.log(person.name);
```csharp
using Apache.Fory;
-[ForyObject]
+[ForyStruct]
public sealed class Person
{
public string Name { get; set; } = string.Empty;
diff --git a/benchmarks/csharp/BenchmarkModels.cs
b/benchmarks/csharp/BenchmarkModels.cs
index d4ee54e0f..a7058c994 100644
--- a/benchmarks/csharp/BenchmarkModels.cs
+++ b/benchmarks/csharp/BenchmarkModels.cs
@@ -21,7 +21,7 @@ using ProtoBuf;
namespace Apache.Fory.Benchmarks.CSharp;
-[ForyObject]
+[ForyStruct]
[MessagePackObject(keyAsPropertyName: true)]
[ProtoContract]
public sealed class NumericStruct
@@ -75,7 +75,7 @@ public sealed class NumericStruct
public int F12 { get; set; }
}
-[ForyObject]
+[ForyStruct]
[MessagePackObject(keyAsPropertyName: true)]
[ProtoContract]
public sealed class NumericStructList
@@ -85,7 +85,7 @@ public sealed class NumericStructList
public List<NumericStruct> Values { get; set; } = [];
}
-[ForyObject]
+[ForyStruct]
[MessagePackObject(keyAsPropertyName: true)]
[ProtoContract]
public sealed class Sample
@@ -179,7 +179,7 @@ public sealed class Sample
public string String { get; set; } = string.Empty;
}
-[ForyObject]
+[ForyStruct]
[MessagePackObject(keyAsPropertyName: true)]
[ProtoContract]
public sealed class SampleList
@@ -189,7 +189,7 @@ public sealed class SampleList
public List<Sample> Values { get; set; } = [];
}
-[ForyObject]
+[ForyStruct]
[ProtoContract]
public enum Player
{
@@ -199,7 +199,7 @@ public enum Player
Flash,
}
-[ForyObject]
+[ForyStruct]
[ProtoContract]
public enum MediaSize
{
@@ -209,7 +209,7 @@ public enum MediaSize
Large,
}
-[ForyObject]
+[ForyStruct]
[MessagePackObject(keyAsPropertyName: true)]
[ProtoContract]
public sealed class Media
@@ -263,7 +263,7 @@ public sealed class Media
public string Copyright { get; set; } = string.Empty;
}
-[ForyObject]
+[ForyStruct]
[MessagePackObject(keyAsPropertyName: true)]
[ProtoContract]
public sealed class Image
@@ -289,7 +289,7 @@ public sealed class Image
public MediaSize Size { get; set; }
}
-[ForyObject]
+[ForyStruct]
[MessagePackObject(keyAsPropertyName: true)]
[ProtoContract]
public sealed class MediaContent
@@ -303,7 +303,7 @@ public sealed class MediaContent
public List<Image> Images { get; set; } = [];
}
-[ForyObject]
+[ForyStruct]
[MessagePackObject(keyAsPropertyName: true)]
[ProtoContract]
public sealed class MediaContentList
diff --git a/compiler/README.md b/compiler/README.md
index aece751cd..99c690b0c 100644
--- a/compiler/README.md
+++ b/compiler/README.md
@@ -407,12 +407,12 @@ struct Cat {
Generates classes with:
-- `[ForyObject]` model attributes
+- `[ForyStruct]`, `[ForyEnum]`, and `[ForyUnion]` model attributes
- Auto-properties for schema fields
- Registration helper class and `ToBytes`/`FromBytes` helpers
```csharp
-[ForyObject]
+[ForyStruct]
public sealed partial class Cat
{
public Dog? Friend { get; set; }
diff --git a/compiler/fory_compiler/generators/csharp.py
b/compiler/fory_compiler/generators/csharp.py
index c5060d123..c9d1716fa 100644
--- a/compiler/fory_compiler/generators/csharp.py
+++ b/compiler/fory_compiler/generators/csharp.py
@@ -675,7 +675,7 @@ class CSharpGenerator(BaseGenerator):
comment = self.format_type_id_comment(enum, f"{ind}//")
if comment:
lines.append(comment)
- lines.append(f"{ind}[ForyObject]")
+ lines.append(f"{ind}[ForyEnum]")
lines.append(f"{ind}public enum
{self.safe_type_identifier(enum.name)}")
lines.append(f"{ind}{{")
@@ -704,6 +704,7 @@ class CSharpGenerator(BaseGenerator):
comment = self.format_type_id_comment(union, f"{ind}//")
if comment:
lines.append(comment)
+ lines.append(f"{ind}[ForyUnion]")
lines.append(f"{ind}public sealed class {type_name} : Union")
lines.append(f"{ind}{{")
lines.append(f"{ind}{self.indent_str}public enum {case_enum}")
@@ -823,9 +824,9 @@ class CSharpGenerator(BaseGenerator):
if comment:
lines.append(comment)
if self.get_effective_evolving(message):
- lines.append(f"{ind}[ForyObject]")
+ lines.append(f"{ind}[ForyStruct]")
else:
- lines.append(f"{ind}[ForyObject(Evolving = false)]")
+ lines.append(f"{ind}[ForyStruct(Evolving = false)]")
lines.append(f"{ind}public sealed partial class {type_name}")
lines.append(f"{ind}{{")
diff --git a/compiler/fory_compiler/tests/test_csharp_generator.py
b/compiler/fory_compiler/tests/test_csharp_generator.py
index f84d5d87d..0c1c78a9b 100644
--- a/compiler/fory_compiler/tests/test_csharp_generator.py
+++ b/compiler/fory_compiler/tests/test_csharp_generator.py
@@ -69,6 +69,32 @@ def test_csharp_namespace_fallback_to_package():
assert "namespace com.example.models;" in file.content
+def test_csharp_semantic_model_attributes():
+ file = generate(
+ """
+ package example;
+
+ enum Status {
+ READY = 1;
+ }
+
+ union Choice {
+ string text = 1;
+ }
+
+ message Envelope {
+ Status status = 1;
+ Choice choice = 2;
+ }
+ """
+ )
+
+ assert "[ForyEnum]" in file.content
+ assert "[ForyUnion]" in file.content
+ assert "[ForyStruct]" in file.content
+ assert "[ForyObject]" not in file.content
+
+
def test_csharp_registration_uses_fdl_package_for_name_registration():
file = generate(
"""
diff --git a/csharp/README.md b/csharp/README.md
index 8b80b8f2b..20df35242 100644
--- a/csharp/README.md
+++ b/csharp/README.md
@@ -10,7 +10,7 @@ The C# implementation provides high-performance object graph
serialization for .
- High-performance binary serialization for .NET 8+
- Cross-language compatibility with Java, Python, C++, Go, Rust, and JavaScript
-- Source-generator-based serializers for `[ForyObject]` types
+- Source-generator-based serializers for `[ForyStruct]` types, plus
`[ForyEnum]` and `[ForyUnion]` registration
- Field-level schema descriptors with `[ForyField(Type = typeof(...))]`
- Optional shared/circular reference tracking (`TrackRef(true)`)
- Compatible mode for schema evolution
@@ -27,7 +27,7 @@ The C# implementation provides high-performance object graph
serialization for .
### Add Apache Fory™ C\#
-From NuGet, reference the single `Apache.Fory` package. It includes the
runtime plus the source generator for `[ForyObject]` types.
+From NuGet, reference the single `Apache.Fory` package. It includes the
runtime plus the source generator for `[ForyStruct]`, `[ForyEnum]`, and
`[ForyUnion]` types.
```xml
<ItemGroup>
@@ -52,7 +52,7 @@ For local development against this repository, reference the
runtime project and
```csharp
using Apache.Fory;
-[ForyObject]
+[ForyStruct]
public sealed class User
{
public long Id { get; set; }
@@ -78,17 +78,17 @@ User decoded = fory.Deserialize<User>(payload);
### 1. Object Graph Serialization
-`[ForyObject]` types are serialized with generated serializers.
+`[ForyStruct]` types are serialized with generated serializers.
```csharp
-[ForyObject]
+[ForyStruct]
public sealed class Address
{
public string Street { get; set; } = string.Empty;
public int Zip { get; set; }
}
-[ForyObject]
+[ForyStruct]
public sealed class Person
{
public long Id { get; set; }
@@ -106,7 +106,7 @@ fory.Register<Person>(101);
Enable reference tracking to preserve object identity.
```csharp
-[ForyObject]
+[ForyStruct]
public sealed class Node
{
public int Value { get; set; }
@@ -128,13 +128,13 @@
System.Diagnostics.Debug.Assert(object.ReferenceEquals(decoded, decoded.Next));
Compatible mode allows schema changes between writer and reader.
```csharp
-[ForyObject]
+[ForyStruct]
public sealed class OneField
{
public string? F1 { get; set; }
}
-[ForyObject]
+[ForyStruct]
public sealed class TwoFields
{
public string F1 { get; set; } = string.Empty;
diff --git a/csharp/src/Fory.Generator/AnalyzerReleases.Unshipped.md
b/csharp/src/Fory.Generator/AnalyzerReleases.Unshipped.md
index 8bec692b5..d67ff3684 100644
--- a/csharp/src/Fory.Generator/AnalyzerReleases.Unshipped.md
+++ b/csharp/src/Fory.Generator/AnalyzerReleases.Unshipped.md
@@ -2,7 +2,8 @@
Rule ID | Category | Severity | Notes
--------|----------|----------|------
-FORY001 | Fory | Error | Generic types are not supported by ForyObject
generator
+FORY001 | Fory | Error | Generic types are not supported by the Fory source
generator
FORY002 | Fory | Error | Missing parameterless constructor
FORY003 | Fory | Error | Unsupported Field encoding
FORY004 | Fory | Error | Invalid Fory field id
+FORY005 | Fory | Error | Invalid Fory union type
diff --git a/csharp/src/Fory.Generator/ForyObjectGenerator.cs
b/csharp/src/Fory.Generator/ForyModelGenerator.cs
similarity index 96%
rename from csharp/src/Fory.Generator/ForyObjectGenerator.cs
rename to csharp/src/Fory.Generator/ForyModelGenerator.cs
index cf320aac2..05f5faefb 100644
--- a/csharp/src/Fory.Generator/ForyObjectGenerator.cs
+++ b/csharp/src/Fory.Generator/ForyModelGenerator.cs
@@ -25,7 +25,7 @@ using Microsoft.CodeAnalysis.Text;
namespace Apache.Fory.Generator;
[Generator(LanguageNames.CSharp)]
-public sealed class ForyObjectGenerator : IIncrementalGenerator
+public sealed class ForyModelGenerator : IIncrementalGenerator
{
private static readonly SymbolDisplayFormat FullNameFormat =
SymbolDisplayFormat.FullyQualifiedFormat.WithMiscellaneousOptions(
@@ -33,8 +33,8 @@ public sealed class ForyObjectGenerator :
IIncrementalGenerator
private static readonly DiagnosticDescriptor GenericTypeNotSupported = new(
id: "FORY001",
- title: "Generic types are not supported by ForyObject generator",
- messageFormat: "Type '{0}' is generic and is not supported by
[ForyObject]",
+ title: "Generic types are not supported by the Fory source generator",
+ messageFormat: "Type '{0}' is generic and is not supported by
generated Fory attributes",
category: "Fory",
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true);
@@ -42,7 +42,7 @@ public sealed class ForyObjectGenerator :
IIncrementalGenerator
private static readonly DiagnosticDescriptor MissingCtor = new(
id: "FORY002",
title: "Missing parameterless constructor",
- messageFormat: "Class '{0}' must declare an accessible parameterless
constructor for [ForyObject]",
+ messageFormat: "Class '{0}' must declare an accessible parameterless
constructor for [ForyStruct]",
category: "Fory",
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true);
@@ -63,12 +63,19 @@ public sealed class ForyObjectGenerator :
IIncrementalGenerator
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true);
+ private static readonly DiagnosticDescriptor InvalidUnionType = new(
+ id: "FORY005",
+ title: "Invalid Fory union type",
+ messageFormat: "Class '{0}' must derive from Apache.Fory.Union for
[ForyUnion]",
+ category: "Fory",
+ defaultSeverity: DiagnosticSeverity.Error,
+ isEnabledByDefault: true);
+
public void Initialize(IncrementalGeneratorInitializationContext context)
{
IncrementalValuesProvider<TypeModel?> typeModels =
context.SyntaxProvider
- .ForAttributeWithMetadataName(
- "Apache.Fory.ForyObjectAttribute",
- static (node, _) => node is TypeDeclarationSyntax || node is
EnumDeclarationSyntax,
+ .CreateSyntaxProvider(
+ static (node, _) => HasCandidateAttributes(node),
static (syntaxContext, ct) => BuildTypeModel(syntaxContext,
ct))
.Where(static m => m is not null);
@@ -77,6 +84,16 @@ public sealed class ForyObjectGenerator :
IIncrementalGenerator
static (spc, models) => Emit(spc, models));
}
+ private static bool HasCandidateAttributes(SyntaxNode node)
+ {
+ return node switch
+ {
+ TypeDeclarationSyntax typeDeclaration =>
typeDeclaration.AttributeLists.Count > 0,
+ EnumDeclarationSyntax enumDeclaration =>
enumDeclaration.AttributeLists.Count > 0,
+ _ => false,
+ };
+ }
+
private static void Emit(SourceProductionContext context,
ImmutableArray<TypeModel?> maybeModels)
{
if (maybeModels.IsDefaultOrEmpty)
@@ -139,6 +156,11 @@ public sealed class ForyObjectGenerator :
IIncrementalGenerator
sb.AppendLine(
$"
global::Apache.Fory.TypeResolver.RegisterGenerated<{model.TypeName},
global::Apache.Fory.EnumSerializer<{model.TypeName}>>();");
}
+ else if (model.Kind == DeclKind.Union)
+ {
+ sb.AppendLine(
+ $"
global::Apache.Fory.TypeResolver.RegisterGenerated<{model.TypeName},
global::Apache.Fory.UnionSerializer<{model.TypeName}>>();");
+ }
else
{
sb.AppendLine(
@@ -2112,23 +2134,43 @@ public sealed class ForyObjectGenerator :
IIncrementalGenerator
return BoolLiteral(member.NeedsFieldTypeInfo);
}
- private static TypeModel? BuildTypeModel(GeneratorAttributeSyntaxContext
context, CancellationToken cancellationToken)
+ private static TypeModel? BuildTypeModel(GeneratorSyntaxContext context,
CancellationToken cancellationToken)
{
_ = cancellationToken;
- if (context.TargetSymbol is not INamedTypeSymbol typeSymbol)
+ if (context.SemanticModel.GetDeclaredSymbol(context.Node,
cancellationToken) is not INamedTypeSymbol typeSymbol)
{
return null;
}
- if (typeSymbol.TypeParameters.Length > 0)
+ ForyAttributeKind attributeKind = GetForyAttributeKind(typeSymbol);
+ if (attributeKind == ForyAttributeKind.None)
{
return null;
}
string typeName = typeSymbol.ToDisplayString(FullNameFormat);
+ if (typeSymbol.TypeParameters.Length > 0)
+ {
+ return new TypeModel(
+ typeName,
+ string.Empty,
+ DeclKind.Unknown,
+ ImmutableArray<MemberModel>.Empty,
+ ImmutableArray<MemberModel>.Empty,
+ ImmutableArray.Create(Diagnostic.Create(
+ GenericTypeNotSupported,
+ typeSymbol.Locations.FirstOrDefault(),
+ typeName)));
+ }
+
string serializerName = "__ForySerializer_" +
Sanitize(typeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat));
- if (typeSymbol.TypeKind == TypeKind.Enum)
+ if (attributeKind == ForyAttributeKind.Enum)
{
+ if (typeSymbol.TypeKind != TypeKind.Enum)
+ {
+ return null;
+ }
+
return new TypeModel(
typeName,
serializerName,
@@ -2138,6 +2180,36 @@ public sealed class ForyObjectGenerator :
IIncrementalGenerator
ImmutableArray<Diagnostic>.Empty);
}
+ if (attributeKind == ForyAttributeKind.Union)
+ {
+ if (typeSymbol.TypeKind != TypeKind.Class)
+ {
+ return null;
+ }
+
+ if (!IsUnionType(typeSymbol))
+ {
+ return new TypeModel(
+ typeName,
+ serializerName,
+ DeclKind.Union,
+ ImmutableArray<MemberModel>.Empty,
+ ImmutableArray<MemberModel>.Empty,
+ ImmutableArray.Create(Diagnostic.Create(
+ InvalidUnionType,
+ typeSymbol.Locations.FirstOrDefault(),
+ typeName)));
+ }
+
+ return new TypeModel(
+ typeName,
+ serializerName,
+ DeclKind.Union,
+ ImmutableArray<MemberModel>.Empty,
+ ImmutableArray<MemberModel>.Empty,
+ ImmutableArray<Diagnostic>.Empty);
+ }
+
DeclKind kind = typeSymbol.TypeKind switch
{
TypeKind.Struct => DeclKind.Struct,
@@ -2152,7 +2224,16 @@ public sealed class ForyObjectGenerator :
IIncrementalGenerator
if (kind == DeclKind.Class &&
!HasAccessibleParameterlessCtor(typeSymbol))
{
- return null;
+ return new TypeModel(
+ typeName,
+ serializerName,
+ kind,
+ ImmutableArray<MemberModel>.Empty,
+ ImmutableArray<MemberModel>.Empty,
+ ImmutableArray.Create(Diagnostic.Create(
+ MissingCtor,
+ typeSymbol.Locations.FirstOrDefault(),
+ typeName)));
}
List<Diagnostic> diagnostics = [];
@@ -2219,6 +2300,30 @@ public sealed class ForyObjectGenerator :
IIncrementalGenerator
return new TypeModel(typeName, serializerName, kind, ordered, sorted,
diagnostics.ToImmutableArray());
}
+ private static ForyAttributeKind GetForyAttributeKind(INamedTypeSymbol
typeSymbol)
+ {
+ foreach (AttributeData attribute in typeSymbol.GetAttributes())
+ {
+ string? attrName = attribute.AttributeClass?.ToDisplayString();
+ if (string.Equals(attrName, "Apache.Fory.ForyStructAttribute",
StringComparison.Ordinal))
+ {
+ return ForyAttributeKind.Struct;
+ }
+
+ if (string.Equals(attrName, "Apache.Fory.ForyEnumAttribute",
StringComparison.Ordinal))
+ {
+ return ForyAttributeKind.Enum;
+ }
+
+ if (string.Equals(attrName, "Apache.Fory.ForyUnionAttribute",
StringComparison.Ordinal))
+ {
+ return ForyAttributeKind.Union;
+ }
+ }
+
+ return ForyAttributeKind.None;
+ }
+
private static MemberModel? BuildMemberModel(
string name,
ITypeSymbol memberType,
@@ -3624,6 +3729,15 @@ public sealed class ForyObjectGenerator :
IIncrementalGenerator
Class,
Struct,
Enum,
+ Union,
+ }
+
+ private enum ForyAttributeKind
+ {
+ None,
+ Struct,
+ Enum,
+ Union,
}
private enum DynamicAnyKind
diff --git a/csharp/src/Fory/Attributes.cs b/csharp/src/Fory/Attributes.cs
index e18be220d..ec3553c28 100644
--- a/csharp/src/Fory/Attributes.cs
+++ b/csharp/src/Fory/Attributes.cs
@@ -18,10 +18,10 @@
namespace Apache.Fory;
/// <summary>
-/// Marks a class, struct, or enum as a generated Fory object type.
+/// Marks a class or struct as a generated Fory struct type.
/// </summary>
-[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct |
AttributeTargets.Enum)]
-public sealed class ForyObjectAttribute : Attribute
+[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
+public sealed class ForyStructAttribute : Attribute
{
/// <summary>
/// Whether the annotated struct should use schema evolution metadata in
compatible mode.
@@ -29,6 +29,22 @@ public sealed class ForyObjectAttribute : Attribute
public bool Evolving { get; set; } = true;
}
+/// <summary>
+/// Marks an enum as a generated Fory enum type.
+/// </summary>
+[AttributeUsage(AttributeTargets.Enum)]
+public sealed class ForyEnumAttribute : Attribute
+{
+}
+
+/// <summary>
+/// Marks a <see cref="Union"/> subclass as a generated Fory union type.
+/// </summary>
+[AttributeUsage(AttributeTargets.Class)]
+public sealed class ForyUnionAttribute : Attribute
+{
+}
+
/// <summary>
/// Overrides generated serializer behavior for a field or property.
/// </summary>
diff --git a/csharp/src/Fory/TypeInfo.cs b/csharp/src/Fory/TypeInfo.cs
index 95c47ec86..65a040a21 100644
--- a/csharp/src/Fory/TypeInfo.cs
+++ b/csharp/src/Fory/TypeInfo.cs
@@ -169,7 +169,7 @@ public sealed class TypeInfo
}
Type structType = Nullable.GetUnderlyingType(type) ?? type;
- ForyObjectAttribute? attribute =
structType.GetCustomAttribute<ForyObjectAttribute>();
+ ForyStructAttribute? attribute =
structType.GetCustomAttribute<ForyStructAttribute>();
return attribute?.Evolving ?? true;
}
diff --git a/csharp/tests/Fory.Tests/ForyGeneratorTests.cs
b/csharp/tests/Fory.Tests/ForyGeneratorTests.cs
index 5c2a32a22..efca15158 100644
--- a/csharp/tests/Fory.Tests/ForyGeneratorTests.cs
+++ b/csharp/tests/Fory.Tests/ForyGeneratorTests.cs
@@ -24,6 +24,57 @@ namespace Apache.Fory.Tests;
public sealed class ForyGeneratorTests
{
+ [Fact]
+ public void SplitAttributesCompile()
+ {
+ const string source = """
+ using Apache.Fory;
+
+ namespace GeneratedDiagnostics;
+
+ [ForyEnum]
+ public enum Status
+ {
+ Ready,
+ Done,
+ }
+
+ [ForyUnion]
+ public sealed class Choice : Union
+ {
+ private Choice(int index, object? value)
+ : base(index, value)
+ {
+ }
+
+ public static Choice Of(int index, object? value)
+ {
+ return new Choice(index, value);
+ }
+
+ public static Choice Text(string value)
+ {
+ return new Choice(1, value);
+ }
+ }
+
+ [ForyStruct]
+ public sealed class Envelope
+ {
+ public Status Status { get; set; }
+ public Choice Choice { get; set; } = Choice.Text(string.Empty);
+ }
+ """;
+
+ CSharpCompilation compilation = CreateCompilation(source);
+ GeneratorDriver driver = CSharpGeneratorDriver.Create(new
ForyModelGenerator());
+ driver.RunGeneratorsAndUpdateCompilation(compilation, out Compilation
output, out ImmutableArray<Diagnostic> diagnostics);
+
+ Assert.DoesNotContain(
+ diagnostics.Concat(output.GetDiagnostics()),
+ diagnostic => diagnostic.Severity == DiagnosticSeverity.Error);
+ }
+
[Fact]
public void NegativeForyFieldIdReportsDiagnostic()
{
@@ -32,7 +83,7 @@ public sealed class ForyGeneratorTests
namespace GeneratedDiagnostics;
- [ForyObject]
+ [ForyStruct]
public sealed class InvalidFieldId
{
[ForyField(-1)]
@@ -41,7 +92,7 @@ public sealed class ForyGeneratorTests
""";
CSharpCompilation compilation = CreateCompilation(source);
- GeneratorDriver driver = CSharpGeneratorDriver.Create(new
ForyObjectGenerator());
+ GeneratorDriver driver = CSharpGeneratorDriver.Create(new
ForyModelGenerator());
driver = driver.RunGeneratorsAndUpdateCompilation(compilation, out
Compilation output, out ImmutableArray<Diagnostic> diagnostics);
ImmutableArray<Diagnostic> generatorDiagnostics =
driver.GetRunResult().Diagnostics;
@@ -56,7 +107,7 @@ public sealed class ForyGeneratorTests
.Split(Path.PathSeparator)
.Select(path => MetadataReference.CreateFromFile(path));
MetadataReference foryReference =
-
MetadataReference.CreateFromFile(typeof(ForyObjectAttribute).Assembly.Location);
+
MetadataReference.CreateFromFile(typeof(ForyStructAttribute).Assembly.Location);
return CSharpCompilation.Create(
"ForyGeneratorDiagnostics",
diff --git a/csharp/tests/Fory.Tests/ForyRuntimeTests.cs
b/csharp/tests/Fory.Tests/ForyRuntimeTests.cs
index 0b312164b..0fcee13b3 100644
--- a/csharp/tests/Fory.Tests/ForyRuntimeTests.cs
+++ b/csharp/tests/Fory.Tests/ForyRuntimeTests.cs
@@ -26,7 +26,7 @@ using S = Apache.Fory.Schema.Types;
namespace Apache.Fory.Tests;
-[ForyObject]
+[ForyEnum]
public enum TestColor
{
Green,
@@ -35,14 +35,14 @@ public enum TestColor
White,
}
-[ForyObject]
+[ForyStruct]
public sealed class Address
{
public string Street { get; set; } = string.Empty;
public int Zip { get; set; }
}
-[ForyObject]
+[ForyStruct]
public sealed class Person
{
public long Id { get; set; }
@@ -54,14 +54,14 @@ public sealed class Person
public Dictionary<sbyte, int?> Metadata { get; set; } = [];
}
-[ForyObject]
+[ForyStruct]
public sealed class Node
{
public int Value { get; set; }
public Node? Next { get; set; }
}
-[ForyObject]
+[ForyStruct]
public sealed class FieldOrder
{
public string Z { get; set; } = string.Empty;
@@ -70,7 +70,7 @@ public sealed class FieldOrder
public int C { get; set; }
}
-[ForyObject]
+[ForyStruct]
public sealed class NonPrimitiveFieldOrder
{
[ForyField(20)]
@@ -84,7 +84,7 @@ public sealed class NonPrimitiveFieldOrder
public int IntValue { get; set; }
}
-[ForyObject]
+[ForyStruct]
public sealed class SchemaNumbers
{
[ForyField(Type = typeof(S.Fixed<S.UInt32>))]
@@ -94,21 +94,21 @@ public sealed class SchemaNumbers
public ulong U64Tagged { get; set; }
}
-[ForyObject]
+[ForyStruct]
public sealed class NestedSchemaByName
{
[ForyField(Type = typeof(S.Map<S.Fixed<S.UInt32>,
S.List<S.Tagged<S.UInt64>>>))]
public Dictionary<uint, List<ulong?>?> Values { get; set; } = [];
}
-[ForyObject]
+[ForyStruct]
public sealed class NestedSchemaById
{
[ForyField(3, Type = typeof(S.Map<S.Fixed<S.UInt32>,
S.List<S.Tagged<S.UInt64>>>))]
public Dictionary<uint, List<ulong?>?> Values { get; set; } = [];
}
-[ForyObject]
+[ForyStruct]
public sealed class NestedSchemaSkipWriter
{
[ForyField(Type = typeof(S.Map<S.Fixed<S.UInt32>,
S.List<S.Tagged<S.UInt64>>>))]
@@ -117,76 +117,76 @@ public sealed class NestedSchemaSkipWriter
public int Tail { get; set; }
}
-[ForyObject]
+[ForyStruct]
public sealed class DefaultListSchema
{
public List<int> Values { get; set; } = [];
}
-[ForyObject]
+[ForyStruct]
public sealed class ExplicitArraySchema
{
[ForyField(Type = typeof(S.Array<S.Int32>))]
public int[] Values { get; set; } = [];
}
-[ForyObject]
+[ForyStruct]
public sealed class CompatibleListSchema
{
[ForyField(Type = typeof(S.List<S.Int32>))]
public List<int> Values { get; set; } = [];
}
-[ForyObject]
+[ForyStruct]
public sealed class CompatibleNullableListSchema
{
[ForyField(Type = typeof(S.List<S.Int32>))]
public List<int?> Values { get; set; } = [];
}
-[ForyObject]
+[ForyStruct]
public sealed class CompatibleArraySchema
{
[ForyField(Type = typeof(S.Array<S.Int32>))]
public int[] Values { get; set; } = [];
}
-[ForyObject]
+[ForyStruct]
public sealed class CompatibleUInt32ListSchema
{
[ForyField(Type = typeof(S.List<S.UInt32>))]
public List<uint> Values { get; set; } = [];
}
-[ForyObject]
+[ForyStruct]
public sealed class CompatibleUInt32ArraySchema
{
[ForyField(Type = typeof(S.Array<S.UInt32>))]
public uint[] Values { get; set; } = [];
}
-[ForyObject]
+[ForyStruct]
public sealed class CompatibleUInt32ArrayListCarrierSchema
{
[ForyField(Type = typeof(S.Array<S.UInt32>))]
public List<uint> Values { get; set; } = [];
}
-[ForyObject]
+[ForyStruct]
public sealed class CompatibleNestedListSchema
{
[ForyField(Type = typeof(S.Map<S.String, S.List<S.Int32>>))]
public Dictionary<string, List<int>> Values { get; set; } = [];
}
-[ForyObject]
+[ForyStruct]
public sealed class CompatibleNestedArraySchema
{
[ForyField(Type = typeof(S.Map<S.String, S.Array<S.Int32>>))]
public Dictionary<string, int[]> Values { get; set; } = [];
}
-[ForyObject]
+[ForyStruct]
public sealed class SemanticScalarSchema
{
[ForyField(Type = typeof(S.Int32))]
@@ -199,62 +199,62 @@ public sealed class SemanticScalarSchema
public long TaggedI64 { get; set; }
}
-[ForyObject]
+[ForyStruct]
public sealed class NestedSchemaSkipReader
{
public int Tail { get; set; }
}
-[ForyObject]
+[ForyStruct]
public sealed class OneStringField
{
public string? F1 { get; set; }
}
-[ForyObject]
+[ForyStruct]
public sealed class TwoStringField
{
public string F1 { get; set; } = string.Empty;
public string F2 { get; set; } = string.Empty;
}
-[ForyObject]
+[ForyStruct]
public sealed class EvolvingOverrideValue
{
public string F1 { get; set; } = string.Empty;
}
-[ForyObject(Evolving = false)]
+[ForyStruct(Evolving = false)]
public sealed class FixedOverrideValue
{
public string F1 { get; set; } = string.Empty;
}
-[ForyObject]
+[ForyStruct]
public sealed class OneStringFieldListHolder
{
public List<OneStringField?> Items { get; set; } = [];
}
-[ForyObject]
+[ForyStruct]
public sealed class TwoStringFieldListHolder
{
public List<TwoStringField?> Items { get; set; } = [];
}
-[ForyObject]
+[ForyStruct]
public sealed class OneStringFieldMapHolder
{
public Dictionary<string, OneStringField?> Items { get; set; } = [];
}
-[ForyObject]
+[ForyStruct]
public sealed class TwoStringFieldMapHolder
{
public Dictionary<string, TwoStringField?> Items { get; set; } = [];
}
-[ForyObject]
+[ForyStruct]
public sealed class UnsignedFields
{
public byte U8 { get; set; }
@@ -267,7 +267,7 @@ public sealed class UnsignedFields
public ulong? U64Nullable { get; set; }
}
-[ForyObject]
+[ForyStruct]
public sealed class StructWithEnum
{
public string Name { get; set; } = string.Empty;
@@ -275,19 +275,19 @@ public sealed class StructWithEnum
public int Value { get; set; }
}
-[ForyObject]
+[ForyStruct]
public sealed class StructWithNullableMap
{
public NullableKeyDictionary<string, string?> Data { get; set; } = new();
}
-[ForyObject]
+[ForyStruct]
public sealed class StructWithUnion2
{
public Union2<string, long> Union { get; set; } = Union2<string,
long>.OfT1(string.Empty);
}
-[ForyObject]
+[ForyStruct]
public sealed class DynamicAnyHolder
{
public object? AnyValue { get; set; }
@@ -295,7 +295,7 @@ public sealed class DynamicAnyHolder
public Dictionary<object, object?> AnyMap { get; set; } = [];
}
-[ForyObject]
+[ForyStruct]
public sealed class DictionaryContainerHolder
{
public Dictionary<string, int> DictionaryField { get; set; } = [];
@@ -304,7 +304,7 @@ public sealed class DictionaryContainerHolder
public ConcurrentDictionary<string, int> ConcurrentField { get; set; } =
new();
}
-[ForyObject]
+[ForyStruct]
public sealed class CollectionContainerHolder
{
public LinkedList<int> LinkedListField { get; set; } = new();
@@ -314,7 +314,7 @@ public sealed class CollectionContainerHolder
public Stack<int> StackField { get; set; } = new();
}
-[ForyObject]
+[ForyStruct]
public sealed class NestedTypedContainers
{
public List<List<string>> NestedLists { get; set; } = [];
diff --git a/csharp/tests/Fory.Tests/RuntimeEdgeCaseTests.cs
b/csharp/tests/Fory.Tests/RuntimeEdgeCaseTests.cs
index 3b3011a3f..8e8f38ba1 100644
--- a/csharp/tests/Fory.Tests/RuntimeEdgeCaseTests.cs
+++ b/csharp/tests/Fory.Tests/RuntimeEdgeCaseTests.cs
@@ -21,7 +21,7 @@ using ForyRuntime = Apache.Fory.Fory;
namespace Apache.Fory.Tests;
-[ForyObject]
+[ForyStruct]
public sealed class TimeEnvelope
{
public DateOnly Date { get; set; }
@@ -34,7 +34,7 @@ public sealed class TimeEnvelope
public List<TimeSpan> Durations { get; set; } = [];
}
-[ForyObject]
+[ForyStruct]
public sealed class NullableEnvelope
{
public int? Int32Value { get; set; }
@@ -43,14 +43,14 @@ public sealed class NullableEnvelope
public TestColor? Color { get; set; }
}
-[ForyObject]
+[ForyStruct]
public sealed class CustomPayload
{
public int Id { get; set; }
public string Marker { get; set; } = string.Empty;
}
-[ForyObject]
+[ForyStruct]
public sealed class DecimalEnvelope
{
public ForyDecimal Exact { get; set; }
diff --git a/csharp/tests/Fory.XlangPeer/Program.cs
b/csharp/tests/Fory.XlangPeer/Program.cs
index 2fad7dfbf..fad0af740 100644
--- a/csharp/tests/Fory.XlangPeer/Program.cs
+++ b/csharp/tests/Fory.XlangPeer/Program.cs
@@ -1135,7 +1135,7 @@ internal static class Program
}
}
-[ForyObject]
+[ForyEnum]
public enum Color
{
Green,
@@ -1144,13 +1144,13 @@ public enum Color
White,
}
-[ForyObject]
+[ForyStruct]
public sealed class Item
{
public string Name { get; set; } = string.Empty;
}
-[ForyObject]
+[ForyStruct]
public sealed class SimpleStruct
{
public Dictionary<int, double> F1 { get; set; } = [];
@@ -1164,19 +1164,19 @@ public sealed class SimpleStruct
public int Last { get; set; }
}
-[ForyObject]
+[ForyStruct]
public sealed class EvolvingOverrideStruct
{
public string F1 { get; set; } = string.Empty;
}
-[ForyObject(Evolving = false)]
+[ForyStruct(Evolving = false)]
public sealed class FixedOverrideStruct
{
public string F1 { get; set; } = string.Empty;
}
-[ForyObject]
+[ForyStruct]
public sealed class Item1
{
public int F1 { get; set; }
@@ -1187,31 +1187,31 @@ public sealed class Item1
public int F6 { get; set; }
}
-[ForyObject]
+[ForyStruct]
public sealed class StructWithList
{
public List<string?> Items { get; set; } = [];
}
-[ForyObject]
+[ForyStruct]
public sealed class StructWithMap
{
public NullableKeyDictionary<string, string?> Data { get; set; } = new();
}
-[ForyObject]
+[ForyStruct]
public sealed class StructWithUnion2
{
public Union2<string, long> Union { get; set; } = Union2<string,
long>.OfT1(string.Empty);
}
-[ForyObject]
+[ForyStruct]
public sealed class MyStruct
{
public int Id { get; set; }
}
-[ForyObject]
+[ForyStruct]
public sealed class MyExt
{
public int Id { get; set; }
@@ -1233,7 +1233,7 @@ public sealed class MyExtSerializer : Serializer<MyExt>
}
}
-[ForyObject]
+[ForyStruct]
public sealed class MyWrapper
{
public Color Color { get; set; }
@@ -1241,12 +1241,12 @@ public sealed class MyWrapper
public MyStruct MyStruct { get; set; } = new();
}
-[ForyObject]
+[ForyStruct]
public sealed class EmptyWrapper
{
}
-[ForyObject]
+[ForyStruct]
public sealed class VersionCheckStruct
{
public int F1 { get; set; }
@@ -1254,57 +1254,57 @@ public sealed class VersionCheckStruct
public double F3 { get; set; }
}
-[ForyObject]
+[ForyStruct]
public sealed class Dog
{
public int Age { get; set; }
public string? Name { get; set; }
}
-[ForyObject]
+[ForyStruct]
public sealed class Cat
{
public int Age { get; set; }
public int Lives { get; set; }
}
-[ForyObject]
+[ForyStruct]
public sealed class AnimalListHolder
{
public List<object?> Animals { get; set; } = [];
}
-[ForyObject]
+[ForyStruct]
public sealed class AnimalMapHolder
{
public Dictionary<string, object?> AnimalMap { get; set; } = [];
}
-[ForyObject]
+[ForyStruct]
public sealed class OneFieldStruct
{
public int Value { get; set; }
}
-[ForyObject]
+[ForyStruct]
public sealed class OneStringFieldStruct
{
public string? F1 { get; set; }
}
-[ForyObject]
+[ForyStruct]
public sealed class TwoStringFieldStruct
{
public string F1 { get; set; } = string.Empty;
public string F2 { get; set; } = string.Empty;
}
-[ForyObject]
+[ForyStruct]
public sealed class EmptyStruct
{
}
-[ForyObject]
+[ForyStruct]
public sealed class ReducedPrecisionFloatStruct
{
public Half Float16Value { get; set; }
@@ -1313,28 +1313,28 @@ public sealed class ReducedPrecisionFloatStruct
public List<BFloat16> BFloat16Array { get; set; } = [];
}
-[ForyObject]
+[ForyStruct]
public sealed class CompatibleInt32ListField
{
[ForyField(1, Type = typeof(S.List<S.Fixed<S.Int32>>))]
public List<int> Values { get; set; } = [];
}
-[ForyObject]
+[ForyStruct]
public sealed class CompatibleNullableInt32ListField
{
[ForyField(1, Type = typeof(S.List<S.Fixed<S.Int32>>))]
public List<int?> Values { get; set; } = [];
}
-[ForyObject]
+[ForyStruct]
public sealed class CompatibleInt32ArrayField
{
[ForyField(1, Type = typeof(S.Array<S.Int32>))]
public int[] Values { get; set; } = [];
}
-[ForyObject]
+[ForyEnum]
public enum TestEnum
{
ValueA,
@@ -1342,20 +1342,20 @@ public enum TestEnum
ValueC,
}
-[ForyObject]
+[ForyStruct]
public sealed class OneEnumFieldStruct
{
public TestEnum F1 { get; set; }
}
-[ForyObject]
+[ForyStruct]
public sealed class TwoEnumFieldStruct
{
public TestEnum F1 { get; set; }
public TestEnum F2 { get; set; }
}
-[ForyObject]
+[ForyStruct]
public sealed class NullableComprehensiveSchemaConsistent
{
public sbyte ByteField { get; set; }
@@ -1382,7 +1382,7 @@ public sealed class NullableComprehensiveSchemaConsistent
public NullableKeyDictionary<string, string>? NullableMap { get; set; }
}
-[ForyObject]
+[ForyStruct]
public sealed class NullableComprehensiveCompatible
{
public sbyte ByteField { get; set; }
@@ -1416,42 +1416,42 @@ public sealed class NullableComprehensiveCompatible
public NullableKeyDictionary<string, string> NullableMap2 { get; set; } =
new();
}
-[ForyObject]
+[ForyStruct]
public sealed class RefInnerSchemaConsistent
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
}
-[ForyObject]
+[ForyStruct]
public sealed class RefOuterSchemaConsistent
{
public RefInnerSchemaConsistent? Inner1 { get; set; }
public RefInnerSchemaConsistent? Inner2 { get; set; }
}
-[ForyObject]
+[ForyStruct]
public sealed class RefInnerCompatible
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
}
-[ForyObject]
+[ForyStruct]
public sealed class RefOuterCompatible
{
public RefInnerCompatible? Inner1 { get; set; }
public RefInnerCompatible? Inner2 { get; set; }
}
-[ForyObject]
+[ForyStruct]
public sealed class RefOverrideElement
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
}
-[ForyObject]
+[ForyStruct]
public sealed class RefOverrideContainer
{
public List<RefOverrideElement?> ListField { get; set; } = [];
@@ -1459,14 +1459,14 @@ public sealed class RefOverrideContainer
public Dictionary<string, RefOverrideElement?> MapField { get; set; } = [];
}
-[ForyObject]
+[ForyStruct]
public sealed class CircularRefStruct
{
public string Name { get; set; } = string.Empty;
public CircularRefStruct? SelfRef { get; set; }
}
-[ForyObject]
+[ForyStruct]
public sealed class UnsignedSchemaConsistentSimple
{
[ForyField(Type = typeof(S.Tagged<S.UInt64>))]
@@ -1476,7 +1476,7 @@ public sealed class UnsignedSchemaConsistentSimple
public ulong? U64TaggedNullable { get; set; }
}
-[ForyObject]
+[ForyStruct]
public sealed class UnsignedSchemaConsistent
{
public byte U8Field { get; set; }
@@ -1510,7 +1510,7 @@ public sealed class UnsignedSchemaConsistent
public ulong? U64TaggedNullableField { get; set; }
}
-[ForyObject]
+[ForyStruct]
public sealed class UnsignedSchemaCompatible
{
public byte? U8Field1 { get; set; }
@@ -1545,14 +1545,14 @@ public sealed class UnsignedSchemaCompatible
}
#pragma warning disable CS8714
-[ForyObject]
+[ForyStruct]
public sealed class NestedAnnotatedContainerSchemaConsistent
{
[ForyField(Type = typeof(S.Map<S.Fixed<S.UInt32>,
S.List<S.Tagged<S.UInt64>>>))]
public NullableKeyDictionary<uint?, List<ulong?>?> Values { get; set; } =
new();
}
-[ForyObject]
+[ForyStruct]
public sealed class NestedAnnotatedContainerCompatible
{
[ForyField(Type = typeof(S.Map<S.Fixed<S.UInt32>,
S.List<S.Tagged<S.UInt64>>>))]
diff --git a/docs/compiler/generated-code.md b/docs/compiler/generated-code.md
index ed2d02c05..3c9d8899f 100644
--- a/docs/compiler/generated-code.md
+++ b/docs/compiler/generated-code.md
@@ -771,10 +771,10 @@ C# output is one `.cs` file per schema, for example:
### Type Generation
-Messages generate `[ForyObject]` classes with C# properties and byte helpers:
+Messages generate `[ForyStruct]` classes with C# properties and byte helpers:
```csharp
-[ForyObject]
+[ForyStruct]
public sealed partial class Person
{
public string Name { get; set; } = string.Empty;
@@ -787,9 +787,10 @@ public sealed partial class Person
}
```
-Unions generate `Union` subclasses with typed case helpers:
+Unions generate `[ForyUnion]` `Union` subclasses with typed case helpers:
```csharp
+[ForyUnion]
public sealed class Animal : Union
{
public static Animal Dog(Dog value) { ... }
diff --git a/docs/compiler/index.md b/docs/compiler/index.md
index b61f49917..5d97bb2d9 100644
--- a/docs/compiler/index.md
+++ b/docs/compiler/index.md
@@ -126,9 +126,9 @@ Generated code uses native language constructs:
- Java: Plain POJOs with `@ForyField` annotations
- Python: Dataclasses with type hints
- Go: Structs with struct tags
-- Rust: Structs with `#[derive(ForyObject)]`
+- Rust: Structs with `#[derive(ForyStruct)]`
- C++: Structs with `FORY_STRUCT` macros
-- C#: Classes with `[ForyObject]` and registration helpers
+- C#: `[ForyStruct]` classes, `[ForyEnum]` enums, `[ForyUnion]` unions, and
registration helpers
- JavaScript/TypeScript: Interfaces with registration function
- Swift: Fory model macros with field/case metadata and registration helpers
- Dart: `@ForyStruct` classes with `@ForyField` annotations and registration
helpers
diff --git a/docs/compiler/schema-idl.md b/docs/compiler/schema-idl.md
index f5cd9da4c..e7f59e203 100644
--- a/docs/compiler/schema-idl.md
+++ b/docs/compiler/schema-idl.md
@@ -714,9 +714,9 @@ message Person { // Auto-generated when
enable_auto_type_id = true
| Java | POJO class with getters/setters |
| Python | `@dataclass` class |
| Go | Struct with exported fields |
-| Rust | Struct with `#[derive(ForyObject)]` |
+| Rust | Struct with `#[derive(ForyStruct)]` |
| C++ | Struct with `FORY_STRUCT` macro |
-| C# | `[ForyObject]` class |
+| C# | `[ForyStruct]` class |
| JavaScript/TypeScript | `export interface` declaration |
| Swift | `@ForyStruct` struct or class |
| Dart | `@ForyStruct` `final class` |
diff --git a/docs/guide/csharp/basic-serialization.md
b/docs/guide/csharp/basic-serialization.md
index a7a7edffb..d112990cc 100644
--- a/docs/guide/csharp/basic-serialization.md
+++ b/docs/guide/csharp/basic-serialization.md
@@ -23,19 +23,19 @@ This page covers typed serialization APIs in Apache Fory™
C#.
## Object Graph Serialization
-Use `[ForyObject]` on your classes/structs and register them before use.
+Use `[ForyStruct]` on your classes/structs and register them before use.
```csharp
using Apache.Fory;
-[ForyObject]
+[ForyStruct]
public sealed class Address
{
public string Street { get; set; } = string.Empty;
public int Zip { get; set; }
}
-[ForyObject]
+[ForyStruct]
public sealed class Person
{
public long Id { get; set; }
@@ -122,7 +122,7 @@ fory.Serialize<object?>(dynamicWriter, value);
- Reuse the same `Fory` or `ThreadSafeFory` instance for better performance.
- Primitive types and collections do not require user registration.
-- User `[ForyObject]` and custom serializer types should be registered
explicitly.
+- User `[ForyStruct]`, `[ForyEnum]`, `[ForyUnion]`, and custom serializer
types should be registered explicitly.
## Related Topics
diff --git a/docs/guide/csharp/custom-serializers.md
b/docs/guide/csharp/custom-serializers.md
index 164a32a48..9e7ef768f 100644
--- a/docs/guide/csharp/custom-serializers.md
+++ b/docs/guide/csharp/custom-serializers.md
@@ -19,7 +19,7 @@ license: |
limitations under the License.
---
-Use custom serializers when a type is not generated with `[ForyObject]` or
requires specialized encoding.
+Use custom serializers when a type is not generated with `[ForyStruct]` or
requires specialized encoding.
## Implement `Serializer<T>`
@@ -75,7 +75,7 @@ Point decoded = fory.Deserialize<Point>(payload);
1. Keep serializers deterministic and symmetric.
2. Use varint/fixed/tagged encoding intentionally for integer-heavy payloads.
3. Register custom serializers on all reader/writer peers.
-4. Prefer generated `[ForyObject]` serializers for normal domain models.
+4. Prefer generated `[ForyStruct]` serializers for normal domain models.
## Related Topics
diff --git a/docs/guide/csharp/index.md b/docs/guide/csharp/index.md
index 9fbb06b01..1b556091b 100644
--- a/docs/guide/csharp/index.md
+++ b/docs/guide/csharp/index.md
@@ -26,7 +26,7 @@ Apache Fory™ C# is a high-performance, cross-language
serialization runtime fo
- High performance binary serialization for .NET 8+
- Xlang compatibility with Fory implementations in Java, Python, C++, Go, Rust,
JavaScript/TypeScript, Swift, Dart, Scala, and Kotlin
-- Source-generator-based serializers for `[ForyObject]` types
+- Source-generator-based serializers for `[ForyStruct]` types, plus
`[ForyEnum]` and `[ForyUnion]` registration
- Optional reference tracking for shared and circular object graphs
- Compatible mode for schema evolution
- Thread-safe runtime (`ThreadSafeFory`) for multi-threaded services
@@ -40,7 +40,7 @@ Apache Fory™ C# is a high-performance, cross-language
serialization runtime fo
### Install from NuGet
-Reference the single `Apache.Fory` package. It includes the runtime and the
source generator for `[ForyObject]` types.
+Reference the single `Apache.Fory` package. It includes the runtime and the
source generator for `[ForyStruct]`, `[ForyEnum]`, and `[ForyUnion]` types.
```xml
<ItemGroup>
@@ -53,7 +53,7 @@ Reference the single `Apache.Fory` package. It includes the
runtime and the sour
```csharp
using Apache.Fory;
-[ForyObject]
+[ForyStruct]
public sealed class User
{
public long Id { get; set; }
diff --git a/docs/guide/csharp/references.md b/docs/guide/csharp/references.md
index 1420be04f..772b2535c 100644
--- a/docs/guide/csharp/references.md
+++ b/docs/guide/csharp/references.md
@@ -39,7 +39,7 @@ When enabled:
```csharp
using Apache.Fory;
-[ForyObject]
+[ForyStruct]
public sealed class Node
{
public int Value { get; set; }
diff --git a/docs/guide/csharp/schema-evolution.md
b/docs/guide/csharp/schema-evolution.md
index 10efda027..108634b77 100644
--- a/docs/guide/csharp/schema-evolution.md
+++ b/docs/guide/csharp/schema-evolution.md
@@ -36,13 +36,13 @@ Compatible mode writes type metadata that allows readers
and writers with differ
```csharp
using Apache.Fory;
-[ForyObject]
+[ForyStruct]
public sealed class OneStringField
{
public string? F1 { get; set; }
}
-[ForyObject]
+[ForyStruct]
public sealed class TwoStringField
{
public string F1 { get; set; } = string.Empty;
diff --git a/docs/guide/csharp/schema-metadata.md
b/docs/guide/csharp/schema-metadata.md
index 3941a3824..37ebd33a7 100644
--- a/docs/guide/csharp/schema-metadata.md
+++ b/docs/guide/csharp/schema-metadata.md
@@ -21,15 +21,15 @@ license: |
This page covers field-level serializer configuration for C# generated
serializers.
-## `[ForyObject]` and `[ForyField]`
+## `[ForyStruct]` and `[ForyField]`
-Use `[ForyObject]` to enable source-generated serializers. Use `[ForyField]`
to assign an optional stable non-negative field id or to override the Fory
schema type used for a field.
+Use `[ForyStruct]` to enable source-generated serializers. Use `[ForyField]`
to assign an optional stable non-negative field id or to override the Fory
schema type used for a field.
```csharp
using Apache.Fory;
using S = Apache.Fory.Schema.Types;
-[ForyObject]
+[ForyStruct]
public sealed class Metrics
{
[ForyField(Type = typeof(S.UInt32))]
@@ -48,7 +48,7 @@ public sealed class Metrics
using Apache.Fory;
using S = Apache.Fory.Schema.Types;
-[ForyObject]
+[ForyStruct]
public sealed class NestedMetrics
{
[ForyField(Type = typeof(S.Map<S.Fixed<S.UInt32>,
S.List<S.Tagged<S.UInt64>>>))]
diff --git a/docs/guide/csharp/supported-types.md
b/docs/guide/csharp/supported-types.md
index fed11cd45..e93b59221 100644
--- a/docs/guide/csharp/supported-types.md
+++ b/docs/guide/csharp/supported-types.md
@@ -76,7 +76,7 @@ This page summarizes built-in and generated type support in
Apache Fory™ C#.
## User Types
-- `[ForyObject]` classes/structs/enums via source-generated serializers
+- `[ForyStruct]` classes/structs via source-generated serializers, plus
`[ForyEnum]` enums and `[ForyUnion]` union subclasses
- Custom serializer types registered through `Register<T, TSerializer>(...)`
- `Union` / `Union2<...>` typed union support
diff --git a/docs/guide/csharp/xlang-serialization.md
b/docs/guide/csharp/xlang-serialization.md
index 3c53e52af..d47b44ded 100644
--- a/docs/guide/csharp/xlang-serialization.md
+++ b/docs/guide/csharp/xlang-serialization.md
@@ -36,7 +36,7 @@ Fory fory = Fory.Builder()
## Register with Stable IDs
```csharp
-[ForyObject]
+[ForyStruct]
public sealed class Person
{
public string Name { get; set; } = string.Empty;
diff --git a/docs/guide/dart/xlang-serialization.md
b/docs/guide/dart/xlang-serialization.md
index 38ec6dc09..b8d5d2811 100644
--- a/docs/guide/dart/xlang-serialization.md
+++ b/docs/guide/dart/xlang-serialization.md
@@ -112,7 +112,7 @@ final bytes = fory.serialize(Person()
### CSharp
```csharp
-[ForyObject]
+[ForyStruct]
public sealed class Person
{
public string Name { get; set; } = string.Empty;
diff --git a/docs/guide/go/type-registration.md
b/docs/guide/go/type-registration.md
index 452cdb5be..d651fb1a9 100644
--- a/docs/guide/go/type-registration.md
+++ b/docs/guide/go/type-registration.md
@@ -214,9 +214,9 @@ fory.register(User, typename="example.User")
**Rust**:
```rust
-use fory::{Fory, ForyObject};
+use fory::{Fory, ForyStruct};
-#[derive(ForyObject)]
+#[derive(ForyStruct)]
struct User {
id: i64,
name: String,
diff --git a/docs/guide/python/xlang-serialization.md
b/docs/guide/python/xlang-serialization.md
index cf1bd19b5..a16da3b7b 100644
--- a/docs/guide/python/xlang-serialization.md
+++ b/docs/guide/python/xlang-serialization.md
@@ -79,9 +79,9 @@ Person person = (Person) fory.deserialize(binaryData);
```rust
use fory::Fory;
-use fory::ForyObject;
+use fory::ForyStruct;
-#[derive(ForyObject)]
+#[derive(ForyStruct)]
struct Person {
name: String,
age: i32,
diff --git a/docs/specification/xlang_type_mapping.md
b/docs/specification/xlang_type_mapping.md
index 6769b1ca9..9894bbb1f 100644
--- a/docs/specification/xlang_type_mapping.md
+++ b/docs/specification/xlang_type_mapping.md
@@ -80,15 +80,15 @@ FDL spells them as an encoding modifier plus a semantic
integer type.
| list | 22 | List/Collection
| list/tuple | array
| vector |
slice | Vec
| `List<T>` | `[T]` | `List<T>`
| `List[T]` | `List<T>` |
| set | 23 | Set
| set | /
| set |
fory.Set | Set
| `HashSet<T>` | `Set<T>` | `Set<T>`
| `Set[T]` | `Set<T>` |
| map | 24 | Map
| dict | Map
| unordered_map |
map | HashMap
| `Dictionary<K,V>` | `[K: V]` | `Map<K,
V>` | `Map[K, V]` | `Map<K, V>` |
-| enum | 25 | Enum subclasses
| enum subclasses | /
| enum | /
| enum
| enum | enum | enum
| Scala 3 enum | enum class |
-| named_enum | 26 | Enum subclasses
| enum subclasses | /
| enum | /
| enum
| enum | enum | enum
| Scala 3 enum | enum class |
-| struct | 27 | pojo/record
| data class | object
| struct/class |
struct | struct
| [ForyObject] class/struct | @ForyStruct struct/class |
@ForyStruct class | case class/class | data
class/class |
-| compatible_struct | 28 | pojo/record
| data class | object
| struct/class |
struct | struct
| [ForyObject] class/struct | @ForyStruct struct/class |
@ForyStruct class | case class/class | data
class/class |
-| named_struct | 29 | pojo/record
| data class | object
| struct/class |
struct | struct
| [ForyObject] class/struct | @ForyStruct struct/class |
@ForyStruct class | case class/class | data
class/class |
-| named_compatible_struct | 30 | pojo/record
| data class | object
| struct/class |
struct | struct
| [ForyObject] class/struct | @ForyStruct struct/class |
@ForyStruct class | case class/class | data
class/class |
-| ext | 31 | pojo/record
| data class | object
| struct/class |
struct | struct
| [ForyObject] class/struct | @ForyStruct struct/class |
@ForyStruct class | case class/class | data
class/class |
-| named_ext | 32 | pojo/record
| data class | object
| struct/class |
struct | struct
| [ForyObject] class/struct | @ForyStruct struct/class |
@ForyStruct class | case class/class | data
class/class |
-| union | 33 | Union
| typing.Union | /
| `std::variant<Ts...>` | /
| tagged union enum
| Union subclass | tagged enum | @ForyUnion
class | ADT enum | sealed class |
+| enum | 25 | Enum subclasses
| enum subclasses | /
| enum | /
| enum
| `[ForyEnum]` enum | enum | enum
| Scala 3 enum | enum class |
+| named_enum | 26 | Enum subclasses
| enum subclasses | /
| enum | /
| enum
| `[ForyEnum]` enum | enum | enum
| Scala 3 enum | enum class |
+| struct | 27 | pojo/record
| data class | object
| struct/class |
struct | struct
| `[ForyStruct]` class/struct | @ForyStruct struct/class |
@ForyStruct class | case class/class | data
class/class |
+| compatible_struct | 28 | pojo/record
| data class | object
| struct/class |
struct | struct
| `[ForyStruct]` class/struct | @ForyStruct struct/class |
@ForyStruct class | case class/class | data
class/class |
+| named_struct | 29 | pojo/record
| data class | object
| struct/class |
struct | struct
| `[ForyStruct]` class/struct | @ForyStruct struct/class |
@ForyStruct class | case class/class | data
class/class |
+| named_compatible_struct | 30 | pojo/record
| data class | object
| struct/class |
struct | struct
| `[ForyStruct]` class/struct | @ForyStruct struct/class |
@ForyStruct class | case class/class | data
class/class |
+| ext | 31 | pojo/record
| data class | object
| struct/class |
struct | struct
| `[ForyStruct]` class/struct | @ForyStruct struct/class |
@ForyStruct class | case class/class | data
class/class |
+| named_ext | 32 | pojo/record
| data class | object
| struct/class |
struct | struct
| `[ForyStruct]` class/struct | @ForyStruct struct/class |
@ForyStruct class | case class/class | data
class/class |
+| union | 33 | Union
| typing.Union | /
| `std::variant<Ts...>` | /
| tagged union enum
| `[ForyUnion]` Union subclass | tagged enum | @ForyUnion
class | ADT enum | sealed class |
| none | 36 | null
| None | null
| `std::monostate` |
nil | `()`
| null | nil | null
| null | null |
| duration | 37 | Duration
| timedelta | Number
| duration |
Duration | Duration
| TimeSpan | Duration | Duration
| java.time.Duration | kotlin.time.Duration |
| timestamp | 38 | Instant
| datetime | Number
| std::chrono::nanoseconds |
Time | Timestamp
| DateTime/DateTimeOffset | Date | Timestamp
| java.time.Instant | java.time.Instant |
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]