Skip to content

Commit 70b6386

Browse files
feat(KubernetesJson): Use of SourceGenerationContext outside AOT (#1677)
* feat: initial source gen for json * wip * chore: readd default settings * chore: cleanup * chore: remove source gen from classic as it depends on C#9 support * Update KubernetesClient.Classic.csproj * wip * enable UseStringEnumConverter * chore: make converters public so we can use them in our libraries * fix: recursion and remove converter from source gen * fix: V1StatusObjectViewConverter * wip * wip * wip * fix: rfc3339 json serialization and yaml de/serialization * chore: add namespace * fix: imports * fix: switch output to RFC3339Micro to fit Time and MicroTime * chore: update AOT to match KubernetesYaml * fix aot * Update buildtest.yaml
1 parent 2922627 commit 70b6386

File tree

14 files changed

+402
-135
lines changed

14 files changed

+402
-135
lines changed

src/KubernetesClient.Aot/KubernetesClient.Aot.csproj

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
44
<TargetFrameworks>net8.0;net9.0</TargetFrameworks>
@@ -43,7 +43,11 @@
4343
<Compile Include="..\KubernetesClient\Models\V1Patch.cs" />
4444
<Compile Include="..\KubernetesClient\Models\V1PodTemplateSpec.cs" />
4545
<Compile Include="..\KubernetesClient\Models\V1Status.cs" />
46-
46+
<Compile Include="..\KubernetesClient\KubernetesJson.cs" />
47+
<Compile Include="..\KubernetesClient\SourceGenerationContext.cs" />
48+
<Compile Include="..\KubernetesClient\Models\V1Status.ObjectView.cs" />
49+
<Compile Include="..\KubernetesClient\Models\KubernetesDateTimeOffsetYamlConverter.cs" />
50+
<Compile Include="..\KubernetesClient\Models\KubernetesDateTimeYamlConverter.cs" />
4751
</ItemGroup>
4852
<ItemGroup>
4953
<Compile Include="..\KubernetesClient\ClientSets\ClientSet.cs" />

src/KubernetesClient.Aot/KubernetesJson.cs

Lines changed: 0 additions & 103 deletions
This file was deleted.

src/KubernetesClient.Aot/KubernetesYaml.cs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ internal static class KubernetesYaml
1717
.WithTypeConverter(new IntOrStringYamlConverter())
1818
.WithTypeConverter(new ByteArrayStringYamlConverter())
1919
.WithTypeConverter(new ResourceQuantityYamlConverter())
20+
.WithTypeConverter(new KubernetesDateTimeYamlConverter())
21+
.WithTypeConverter(new KubernetesDateTimeOffsetYamlConverter())
2022
.WithAttemptingUnquotedStringTypeDeserialization()
2123
;
2224

@@ -33,6 +35,8 @@ internal static class KubernetesYaml
3335
.WithTypeConverter(new IntOrStringYamlConverter())
3436
.WithTypeConverter(new ByteArrayStringYamlConverter())
3537
.WithTypeConverter(new ResourceQuantityYamlConverter())
38+
.WithTypeConverter(new KubernetesDateTimeYamlConverter())
39+
.WithTypeConverter(new KubernetesDateTimeOffsetYamlConverter())
3640
.WithEventEmitter(e => new StringQuotingEmitter(e))
3741
.WithEventEmitter(e => new FloatEmitter(e))
3842
.ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitNull)
@@ -56,7 +60,7 @@ public object ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeseria
5660
return null;
5761
}
5862

59-
return Encoding.UTF8.GetBytes(scalar.Value);
63+
return Convert.FromBase64String(scalar.Value);
6064
}
6165
finally
6266
{
@@ -69,8 +73,15 @@ public object ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeseria
6973

7074
public void WriteYaml(IEmitter emitter, object value, Type type, ObjectSerializer serializer)
7175
{
76+
if (value == null)
77+
{
78+
emitter.Emit(new Scalar(string.Empty));
79+
return;
80+
}
81+
7282
var obj = (byte[])value;
73-
emitter?.Emit(new Scalar(Encoding.UTF8.GetString(obj)));
83+
var encoded = Convert.ToBase64String(obj);
84+
emitter.Emit(new Scalar(encoded));
7485
}
7586
}
7687

src/KubernetesClient.Aot/SourceGenerationContext.cs

Lines changed: 0 additions & 12 deletions
This file was deleted.

src/KubernetesClient.Classic/KubernetesClient.Classic.csproj

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
44
<TargetFrameworks>netstandard2.0;net48</TargetFrameworks>
@@ -49,6 +49,9 @@
4949
<Compile Include="..\KubernetesClient\Models\V1PodTemplateSpec.cs" />
5050
<Compile Include="..\KubernetesClient\Models\V1Status.cs" />
5151
<Compile Include="..\KubernetesClient\Models\V1Status.ObjectView.cs" />
52+
<Compile Include="..\KubernetesClient\Models\KubernetesDateTimeOffsetYamlConverter.cs" />
53+
<Compile Include="..\KubernetesClient\Models\KubernetesDateTimeYamlConverter.cs" />
54+
5255

5356
<Compile Include="..\KubernetesClient\KubeConfigModels\ClusterEndpoint.cs" />
5457
<Compile Include="..\KubernetesClient\KubeConfigModels\Context.cs" />

src/KubernetesClient/KubernetesJson.cs

Lines changed: 76 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,17 @@
33
using System.Text.RegularExpressions;
44
using System.Xml;
55

6+
#if NET8_0_OR_GREATER
7+
using System.Text.Json.Serialization.Metadata;
8+
#endif
9+
610
namespace k8s
711
{
812
public static class KubernetesJson
913
{
10-
private static readonly JsonSerializerOptions JsonSerializerOptions = new JsonSerializerOptions();
14+
internal static readonly JsonSerializerOptions JsonSerializerOptions = new JsonSerializerOptions();
1115

12-
private sealed class Iso8601TimeSpanConverter : JsonConverter<TimeSpan>
16+
public sealed class Iso8601TimeSpanConverter : JsonConverter<TimeSpan>
1317
{
1418
public override TimeSpan Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
1519
{
@@ -24,11 +28,11 @@ public override void Write(Utf8JsonWriter writer, TimeSpan value, JsonSerializer
2428
}
2529
}
2630

27-
private sealed class KubernetesDateTimeOffsetConverter : JsonConverter<DateTimeOffset>
31+
public sealed class KubernetesDateTimeOffsetConverter : JsonConverter<DateTimeOffset>
2832
{
29-
private const string RFC3339MicroFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.ffffffK";
30-
private const string RFC3339NanoFormat = "yyyy-MM-dd'T'HH':'mm':'ss.fffffffK";
31-
private const string RFC3339Format = "yyyy'-'MM'-'dd'T'HH':'mm':'ssK";
33+
private const string RFC3339MicroFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.ffffffZ";
34+
private const string RFC3339NanoFormat = "yyyy-MM-dd'T'HH':'mm':'ss.fffffffZ";
35+
private const string RFC3339Format = "yyyy'-'MM'-'dd'T'HH':'mm':'ssZ";
3236

3337
public override DateTimeOffset Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
3438
{
@@ -50,13 +54,22 @@ public override DateTimeOffset Read(ref Utf8JsonReader reader, Type typeToConver
5054
throw new FormatException($"Unable to parse {originalstr} as RFC3339 RFC3339Micro or RFC3339Nano");
5155
}
5256

57+
5358
public override void Write(Utf8JsonWriter writer, DateTimeOffset value, JsonSerializerOptions options)
5459
{
55-
writer.WriteStringValue(value.ToString(RFC3339MicroFormat));
60+
// Output as RFC3339Micro
61+
var date = value.ToUniversalTime();
62+
63+
var basePart = date.ToString("yyyy-MM-dd'T'HH:mm:ss", CultureInfo.InvariantCulture);
64+
var frac = date.ToString(".ffffff", CultureInfo.InvariantCulture)
65+
.TrimEnd('0')
66+
.TrimEnd('.');
67+
68+
writer.WriteStringValue(basePart + frac + "Z");
5669
}
5770
}
5871

59-
private sealed class KubernetesDateTimeConverter : JsonConverter<DateTime>
72+
public sealed class KubernetesDateTimeConverter : JsonConverter<DateTime>
6073
{
6174
private static readonly JsonConverter<DateTimeOffset> UtcConverter = new KubernetesDateTimeOffsetConverter();
6275
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
@@ -72,13 +85,22 @@ public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializer
7285

7386
static KubernetesJson()
7487
{
88+
#if K8S_AOT
89+
// Uses Source Generated IJsonTypeInfoResolver
90+
JsonSerializerOptions.TypeInfoResolver = SourceGenerationContext.Default;
91+
#else
92+
#if NET8_0_OR_GREATER
93+
// Uses Source Generated IJsonTypeInfoResolver when available and falls back to reflection
94+
JsonSerializerOptions.TypeInfoResolver = JsonTypeInfoResolver.Combine(SourceGenerationContext.Default, new DefaultJsonTypeInfoResolver());
95+
#endif
96+
JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
97+
#endif
7598
JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
7699
JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
77100
JsonSerializerOptions.Converters.Add(new Iso8601TimeSpanConverter());
78101
JsonSerializerOptions.Converters.Add(new KubernetesDateTimeConverter());
79102
JsonSerializerOptions.Converters.Add(new KubernetesDateTimeOffsetConverter());
80103
JsonSerializerOptions.Converters.Add(new V1Status.V1StatusObjectViewConverter());
81-
JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
82104
}
83105

84106
/// <summary>
@@ -99,47 +121,92 @@ public static void AddJsonOptions(Action<JsonSerializerOptions> configure)
99121

100122
public static TValue Deserialize<TValue>(string json, JsonSerializerOptions jsonSerializerOptions = null)
101123
{
124+
#if NET8_0_OR_GREATER
125+
var info = (JsonTypeInfo<TValue>)(jsonSerializerOptions ?? JsonSerializerOptions).GetTypeInfo(typeof(TValue));
126+
return JsonSerializer.Deserialize(json, info);
127+
#else
102128
return JsonSerializer.Deserialize<TValue>(json, jsonSerializerOptions ?? JsonSerializerOptions);
129+
#endif
103130
}
104131

105132
public static TValue Deserialize<TValue>(Stream json, JsonSerializerOptions jsonSerializerOptions = null)
106133
{
134+
#if NET8_0_OR_GREATER
135+
var info = (JsonTypeInfo<TValue>)(jsonSerializerOptions ?? JsonSerializerOptions).GetTypeInfo(typeof(TValue));
136+
return JsonSerializer.Deserialize(json, info);
137+
#else
107138
return JsonSerializer.Deserialize<TValue>(json, jsonSerializerOptions ?? JsonSerializerOptions);
139+
#endif
108140
}
109141

110142
public static TValue Deserialize<TValue>(JsonDocument json, JsonSerializerOptions jsonSerializerOptions = null)
111143
{
144+
#if NET8_0_OR_GREATER
145+
var info = (JsonTypeInfo<TValue>)(jsonSerializerOptions ?? JsonSerializerOptions).GetTypeInfo(typeof(TValue));
146+
return JsonSerializer.Deserialize(json, info);
147+
#else
112148
return JsonSerializer.Deserialize<TValue>(json, jsonSerializerOptions ?? JsonSerializerOptions);
149+
#endif
113150
}
114151

115152
public static TValue Deserialize<TValue>(JsonElement json, JsonSerializerOptions jsonSerializerOptions = null)
116153
{
154+
#if NET8_0_OR_GREATER
155+
var info = (JsonTypeInfo<TValue>)(jsonSerializerOptions ?? JsonSerializerOptions).GetTypeInfo(typeof(TValue));
156+
return JsonSerializer.Deserialize(json, info);
157+
#else
117158
return JsonSerializer.Deserialize<TValue>(json, jsonSerializerOptions ?? JsonSerializerOptions);
159+
#endif
118160
}
119161

120162
public static TValue Deserialize<TValue>(JsonNode json, JsonSerializerOptions jsonSerializerOptions = null)
121163
{
164+
#if NET8_0_OR_GREATER
165+
var info = (JsonTypeInfo<TValue>)(jsonSerializerOptions ?? JsonSerializerOptions).GetTypeInfo(typeof(TValue));
166+
return JsonSerializer.Deserialize(json, info);
167+
#else
122168
return JsonSerializer.Deserialize<TValue>(json, jsonSerializerOptions ?? JsonSerializerOptions);
169+
#endif
123170
}
124171

125172
public static string Serialize(object value, JsonSerializerOptions jsonSerializerOptions = null)
126173
{
174+
#if NET8_0_OR_GREATER
175+
var info = (jsonSerializerOptions ?? JsonSerializerOptions).GetTypeInfo(value.GetType());
176+
return JsonSerializer.Serialize(value, info);
177+
#else
127178
return JsonSerializer.Serialize(value, jsonSerializerOptions ?? JsonSerializerOptions);
179+
#endif
128180
}
129181

130182
public static string Serialize(JsonDocument value, JsonSerializerOptions jsonSerializerOptions = null)
131183
{
184+
#if NET8_0_OR_GREATER
185+
var info = (jsonSerializerOptions ?? JsonSerializerOptions).GetTypeInfo(value.GetType());
186+
return JsonSerializer.Serialize(value, info);
187+
#else
132188
return JsonSerializer.Serialize(value, jsonSerializerOptions ?? JsonSerializerOptions);
189+
#endif
133190
}
134191

135192
public static string Serialize(JsonElement value, JsonSerializerOptions jsonSerializerOptions = null)
136193
{
194+
#if NET8_0_OR_GREATER
195+
var info = (jsonSerializerOptions ?? JsonSerializerOptions).GetTypeInfo(value.GetType());
196+
return JsonSerializer.Serialize(value, info);
197+
#else
137198
return JsonSerializer.Serialize(value, jsonSerializerOptions ?? JsonSerializerOptions);
199+
#endif
138200
}
139201

140202
public static string Serialize(JsonNode value, JsonSerializerOptions jsonSerializerOptions = null)
141203
{
204+
#if NET8_0_OR_GREATER
205+
var info = (jsonSerializerOptions ?? JsonSerializerOptions).GetTypeInfo(value.GetType());
206+
return JsonSerializer.Serialize(value, info);
207+
#else
142208
return JsonSerializer.Serialize(value, jsonSerializerOptions ?? JsonSerializerOptions);
209+
#endif
143210
}
144211
}
145212
}

src/KubernetesClient/KubernetesYaml.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ public static class KubernetesYaml
2121
.WithTypeConverter(new IntOrStringYamlConverter())
2222
.WithTypeConverter(new ByteArrayStringYamlConverter())
2323
.WithTypeConverter(new ResourceQuantityYamlConverter())
24+
.WithTypeConverter(new KubernetesDateTimeYamlConverter())
25+
.WithTypeConverter(new KubernetesDateTimeOffsetYamlConverter())
2426
.WithAttemptingUnquotedStringTypeDeserialization()
2527
.WithOverridesFromJsonPropertyAttributes();
2628

@@ -41,6 +43,8 @@ public static class KubernetesYaml
4143
.WithTypeConverter(new IntOrStringYamlConverter())
4244
.WithTypeConverter(new ByteArrayStringYamlConverter())
4345
.WithTypeConverter(new ResourceQuantityYamlConverter())
46+
.WithTypeConverter(new KubernetesDateTimeYamlConverter())
47+
.WithTypeConverter(new KubernetesDateTimeOffsetYamlConverter())
4448
.WithEventEmitter(e => new StringQuotingEmitter(e))
4549
.WithEventEmitter(e => new FloatEmitter(e))
4650
.ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitNull)

0 commit comments

Comments
 (0)