-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement a better union example and allow SerdeTypeOptions on enum (#…
…149) Provides an example on how to write 'externally tagged unions' in serde.net and allows placing SerdeTypeOptions on enum declarations.
- Loading branch information
Showing
10 changed files
with
176 additions
and
107 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,129 +1,90 @@ | ||
using System.Runtime.Serialization; | ||
using System.Diagnostics; | ||
using Serde; | ||
using Serde.Json; | ||
using StaticCs; | ||
|
||
var json = """ | ||
{ "bar": { "_t": "Bar1", "bar": 1 } } | ||
"""; | ||
var bar = JsonSerializer.Deserialize<Foo>(json); | ||
Console.WriteLine(bar); | ||
var a = new BaseType.DerivedA { A = 1 }; | ||
var b = new BaseType.DerivedB { B = "foo" }; | ||
var aSerialized = JsonSerializer.Serialize(a); | ||
var bSerialized = JsonSerializer.Serialize(b); | ||
|
||
[GenerateDeserialize] | ||
partial class Foo { | ||
public required AbstractBar bar; | ||
} | ||
|
||
[GenerateDeserialize] | ||
partial class Bar1 : AbstractBar { | ||
public int bar; | ||
} | ||
Console.WriteLine($"a: {aSerialized}, " + (aSerialized == """{"DerivedA":{"a":1}}""")); | ||
Console.WriteLine($"b: {bSerialized}, " + (bSerialized == """{"DerivedB":{"b":"foo"}}""")); | ||
|
||
[GenerateSerde] | ||
partial class Bar2 : AbstractBar { | ||
public double bar; | ||
} | ||
Console.WriteLine("a: " + (JsonSerializer.Deserialize<BaseType>(aSerialized) == a)); | ||
Console.WriteLine("b: " + (JsonSerializer.Deserialize<BaseType>(bSerialized) == b)); | ||
|
||
abstract partial class AbstractBar : IDeserialize<AbstractBar> | ||
[Closed] | ||
abstract partial record BaseType | ||
{ | ||
static AbstractBar IDeserialize<AbstractBar>.Deserialize<D>(ref D deserializer) | ||
private BaseType() { } | ||
|
||
public sealed partial record DerivedA : BaseType | ||
{ | ||
var visitor = new SerdeVisitor(deserializer); | ||
var fieldNames = new[] | ||
{ | ||
"bar" | ||
}; | ||
return deserializer.DeserializeType<AbstractBar, SerdeVisitor>("AbstractBar", fieldNames, visitor); | ||
public required int A { get; init; } | ||
} | ||
public sealed partial record DerivedB : BaseType | ||
{ | ||
public required string B { get; init; } | ||
} | ||
} | ||
|
||
private sealed class SerdeVisitor : IDeserializeVisitor<AbstractBar> | ||
partial record BaseType : ISerialize<BaseType> | ||
{ | ||
public void Serialize(BaseType value, ISerializer serializer) | ||
{ | ||
private readonly IDeserializer _deserializer; | ||
public SerdeVisitor(IDeserializer deserializer) | ||
var serializeType = serializer.SerializeType("BaseType", 2); | ||
switch (value) | ||
{ | ||
_deserializer = deserializer; | ||
case DerivedA derivedA: | ||
serializeType.SerializeField<DerivedA, DerivedAWrap>(nameof(DerivedA), derivedA); | ||
break; | ||
case DerivedB derivedB: | ||
serializeType.SerializeField<DerivedB, DerivedBWrap>(nameof(DerivedB), derivedB); | ||
break; | ||
} | ||
serializeType.End(); | ||
} | ||
|
||
public string ExpectedTypeName => "AbstractBar"; | ||
[GenerateSerde(Through = nameof(Value))] | ||
private readonly partial record struct DerivedAWrap(DerivedA Value); | ||
|
||
AbstractBar IDeserializeVisitor<AbstractBar>.VisitDictionary<D>(ref D d) | ||
{ | ||
var result = d.TryGetNextKey<string, StringWrap>(out string? key); | ||
if (!result || key != "_t") | ||
{ | ||
throw new InvalidDeserializeValueException("Expected a _t field"); | ||
} | ||
var value = d.GetNextValue<string, StringWrap>(); | ||
var inline = new InlineDeserializer(_deserializer, d); | ||
switch (value) | ||
{ | ||
case "Bar1": | ||
var bar1 = InlineDeserialize<Bar1>(inline); | ||
return bar1; | ||
case "Bar2": | ||
var bar2 = InlineDeserialize<Bar2>(inline); | ||
return bar2; | ||
default: | ||
throw new InvalidDeserializeValueException($"Unexpected value {value}"); | ||
} | ||
} | ||
[GenerateSerde(Through = nameof(Value))] | ||
private readonly partial record struct DerivedBWrap(DerivedB Value); | ||
} | ||
|
||
private static T InlineDeserialize<T>(IDeserializer deserializer) where T : IDeserialize<T> | ||
{ | ||
return T.Deserialize(ref deserializer); | ||
} | ||
partial record BaseType : IDeserialize<BaseType> | ||
{ | ||
public static BaseType Deserialize<D>(ref D deserializer) where D : IDeserializer | ||
{ | ||
return deserializer.DeserializeDictionary<BaseType, DeserializeVisitor>(new DeserializeVisitor()); | ||
} | ||
|
||
private class InlineDeserializer : IDeserializer | ||
[Closed] | ||
[GenerateDeserialize] | ||
[SerdeTypeOptions(MemberFormat = MemberFormat.None)] | ||
private enum KeyNames | ||
{ | ||
private readonly IDeserializer _deserializer; | ||
private IDeserializeDictionary _deserializeDictionary; | ||
DerivedA, | ||
DerivedB, | ||
} | ||
|
||
public InlineDeserializer(IDeserializer deserializer, IDeserializeDictionary deserializeDictionary) | ||
private sealed class DeserializeVisitor : IDeserializeVisitor<BaseType> | ||
{ | ||
public string ExpectedTypeName => nameof(BaseType); | ||
|
||
BaseType IDeserializeVisitor<BaseType>.VisitDictionary<D>(ref D deserializer) | ||
{ | ||
_deserializer = deserializer; | ||
_deserializeDictionary = deserializeDictionary; | ||
deserializer.TryGetNextKey<KeyNames, KeyNamesWrap>(out var type); | ||
switch (type) | ||
{ | ||
case KeyNames.DerivedA: | ||
return deserializer.GetNextValue<DerivedA, DerivedAWrap>(); | ||
case KeyNames.DerivedB: | ||
return deserializer.GetNextValue<DerivedB, DerivedBWrap>(); | ||
default: | ||
throw new InvalidOperationException(); | ||
} | ||
} | ||
|
||
public T DeserializeAny<T, V>(V v) where V : IDeserializeVisitor<T> => throw new NotImplementedException(); | ||
|
||
public T DeserializeBool<T, V>(V v) where V : IDeserializeVisitor<T> => throw new NotImplementedException(); | ||
|
||
public T DeserializeByte<T, V>(V v) where V : IDeserializeVisitor<T> => throw new NotImplementedException(); | ||
|
||
public T DeserializeChar<T, V>(V v) where V : IDeserializeVisitor<T> => throw new NotImplementedException(); | ||
|
||
public T DeserializeDecimal<T, V>(V v) where V : IDeserializeVisitor<T> => throw new NotImplementedException(); | ||
|
||
public T DeserializeDictionary<T, V>(V v) where V : IDeserializeVisitor<T> | ||
=> v.VisitDictionary(ref _deserializeDictionary); | ||
|
||
public T DeserializeDouble<T, V>(V v) where V : IDeserializeVisitor<T> => throw new NotImplementedException(); | ||
|
||
public T DeserializeEnumerable<T, V>(V v) where V : IDeserializeVisitor<T> => throw new NotImplementedException(); | ||
|
||
public T DeserializeFloat<T, V>(V v) where V : IDeserializeVisitor<T> => throw new NotImplementedException(); | ||
|
||
public T DeserializeI16<T, V>(V v) where V : IDeserializeVisitor<T> => throw new NotImplementedException(); | ||
|
||
public T DeserializeI32<T, V>(V v) where V : IDeserializeVisitor<T> => throw new NotImplementedException(); | ||
|
||
public T DeserializeI64<T, V>(V v) where V : IDeserializeVisitor<T> => throw new NotImplementedException(); | ||
|
||
public T DeserializeIdentifier<T, V>(V v) where V : IDeserializeVisitor<T> => throw new NotImplementedException(); | ||
|
||
public T DeserializeNullableRef<T, V>(V v) where V : IDeserializeVisitor<T> => throw new NotImplementedException(); | ||
|
||
public T DeserializeSByte<T, V>(V v) where V : IDeserializeVisitor<T> => throw new NotImplementedException(); | ||
|
||
public T DeserializeString<T, V>(V v) where V : IDeserializeVisitor<T> => throw new NotImplementedException(); | ||
|
||
public T DeserializeType<T, V>(string typeName, ReadOnlySpan<string> fieldNames, V v) where V : IDeserializeVisitor<T> | ||
=> DeserializeDictionary<T, V>(v); | ||
|
||
public T DeserializeU16<T, V>(V v) where V : IDeserializeVisitor<T> => throw new NotImplementedException(); | ||
|
||
public T DeserializeU32<T, V>(V v) where V : IDeserializeVisitor<T> => throw new NotImplementedException(); | ||
|
||
public T DeserializeU64<T, V>(V v) where V : IDeserializeVisitor<T> => throw new NotImplementedException(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
32 changes: 32 additions & 0 deletions
32
...tion.Test/test_output/MemberFormatTests.EnumFormat/ColorEnumWrap.IDeserialize.verified.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
//HintName: ColorEnumWrap.IDeserialize.cs | ||
|
||
#nullable enable | ||
using System; | ||
using Serde; | ||
|
||
partial record struct ColorEnumWrap : Serde.IDeserialize<ColorEnum> | ||
{ | ||
static ColorEnum Serde.IDeserialize<ColorEnum>.Deserialize<D>(ref D deserializer) | ||
{ | ||
var visitor = new SerdeVisitor(); | ||
return deserializer.DeserializeString<ColorEnum, SerdeVisitor>(visitor); | ||
} | ||
|
||
private sealed class SerdeVisitor : Serde.IDeserializeVisitor<ColorEnum> | ||
{ | ||
public string ExpectedTypeName => "ColorEnum"; | ||
|
||
ColorEnum Serde.IDeserializeVisitor<ColorEnum>.VisitString(string s) => s switch | ||
{ | ||
"Red" => ColorEnum.Red, | ||
"Green" => ColorEnum.Green, | ||
"Blue" => ColorEnum.Blue, | ||
_ => throw new InvalidDeserializeValueException("Unexpected enum field name: " + s)}; | ||
ColorEnum Serde.IDeserializeVisitor<ColorEnum>.VisitUtf8Span(System.ReadOnlySpan<byte> s) => s switch | ||
{ | ||
_ when System.MemoryExtensions.SequenceEqual(s, "Red"u8) => ColorEnum.Red, | ||
_ when System.MemoryExtensions.SequenceEqual(s, "Green"u8) => ColorEnum.Green, | ||
_ when System.MemoryExtensions.SequenceEqual(s, "Blue"u8) => ColorEnum.Blue, | ||
_ => throw new InvalidDeserializeValueException("Unexpected enum field name: " + System.Text.Encoding.UTF8.GetString(s))}; | ||
} | ||
} |
20 changes: 20 additions & 0 deletions
20
...ration.Test/test_output/MemberFormatTests.EnumFormat/ColorEnumWrap.ISerialize.verified.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
//HintName: ColorEnumWrap.ISerialize.cs | ||
|
||
#nullable enable | ||
using System; | ||
using Serde; | ||
|
||
partial record struct ColorEnumWrap : Serde.ISerialize | ||
{ | ||
void Serde.ISerialize.Serialize(ISerializer serializer) | ||
{ | ||
var name = Value switch | ||
{ | ||
ColorEnum.Red => "Red", | ||
ColorEnum.Green => "Green", | ||
ColorEnum.Blue => "Blue", | ||
_ => null | ||
}; | ||
serializer.SerializeEnumValue("ColorEnum", name, new Int32Wrap((int)Value)); | ||
} | ||
} |
6 changes: 6 additions & 0 deletions
6
...on.Test/test_output/MemberFormatTests.EnumFormat/ColorEnumWrap.ISerializeWrap.verified.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
//HintName: ColorEnumWrap.ISerializeWrap.cs | ||
|
||
partial record struct ColorEnumWrap : Serde.ISerializeWrap<ColorEnum, ColorEnumWrap> | ||
{ | ||
static ColorEnumWrap Serde.ISerializeWrap<ColorEnum, ColorEnumWrap>.Create(ColorEnum value) => new(value); | ||
} |
20 changes: 20 additions & 0 deletions
20
...tion.Test/test_output/MemberFormatTests.EnumFormat/ColorEnumWrap.ISerialize`1.verified.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
//HintName: ColorEnumWrap.ISerialize`1.cs | ||
|
||
#nullable enable | ||
using System; | ||
using Serde; | ||
|
||
partial record struct ColorEnumWrap : Serde.ISerialize<ColorEnum> | ||
{ | ||
void ISerialize<ColorEnum>.Serialize(ColorEnum value, ISerializer serializer) | ||
{ | ||
var name = value switch | ||
{ | ||
ColorEnum.Red => "Red", | ||
ColorEnum.Green => "Green", | ||
ColorEnum.Blue => "Blue", | ||
_ => null | ||
}; | ||
serializer.SerializeEnumValue("ColorEnum", name, new Int32Wrap((int)value)); | ||
} | ||
} |
3 changes: 3 additions & 0 deletions
3
.../Serde.Generation.Test/test_output/MemberFormatTests.EnumFormat/ColorEnumWrap.verified.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
//HintName: ColorEnumWrap.cs | ||
|
||
readonly partial record struct ColorEnumWrap(ColorEnum Value); |