Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

<PropertyGroup>
<IsPackable>true</IsPackable>
<TargetFrameworks>netstandard2.0;netstandard2.1</TargetFrameworks>
<TargetFramework>net6.0</TargetFramework>
<LangVersion>latest</LangVersion>
<PackageIconUrl>https://camo.githubusercontent.com/fa6d5c12609ed8a3ba1163b96f9e9979b8f59b0d/687474703a2f2f7765732e696f2f566663732f636f6e74656e74</PackageIconUrl>
<Copyright>Copyright (c) .NET Foundation and Contributors</Copyright>
Expand Down
2 changes: 1 addition & 1 deletion src/Docker.DotNet/Docker.DotNet.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@
<PackageReference Include="System.Buffers" Version="4.5.1" />
<PackageReference Include="System.IO.Pipelines" Version="8.0.0" />
<PackageReference Include="System.Net.Http.Json" Version="8.0.0" />
<PackageReference Include="System.Text.Json" Version="8.0.2" />
<PackageReference Include="System.Text.Json" Version="8.0.6" />
</ItemGroup>
</Project>
762 changes: 762 additions & 0 deletions src/Docker.DotNet/DockerDotnetJsonSerializerContext.Generated.cs

Large diffs are not rendered by default.

5 changes: 2 additions & 3 deletions src/Docker.DotNet/EnumerableQueryStringConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,13 @@ public string[] Convert(object o)
var items = new List<string>();
foreach (var e in ((IEnumerable) o))
{
if (e is ValueType ||
e is string)
if (e is ValueType || e is string)
{
items.Add(e.ToString());
}
else
{
items.Add(System.Text.Json.JsonSerializer.Serialize(e));
throw new NotImplementedException();
}
}

Expand Down
20 changes: 0 additions & 20 deletions src/Docker.DotNet/JsonQueryStringConverter.cs

This file was deleted.

36 changes: 17 additions & 19 deletions src/Docker.DotNet/JsonSerializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,11 @@
using System.IO;
using System.IO.Pipelines;
using System.Net.Http;
using System.Net.Http.Json;
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Docker.DotNet.Models;

namespace Docker.DotNet
{
Expand All @@ -17,17 +16,10 @@ namespace Docker.DotNet
/// </summary>
internal class JsonSerializer
{
private readonly JsonSerializerOptions _options = new()
public JsonSerializer()
{
Converters =
{
new JsonEnumMemberConverter<TaskState>(),
new JsonEnumMemberConverter<RestartPolicyKind>(),
new JsonDateTimeConverter(),
new JsonNullableDateTimeConverter(),
new JsonBase64Converter(),
},
};
DockerDotnetJsonSerializerContext.PreserveReflection();
}

// Adapted from https://github.com/dotnet/runtime/issues/33030#issuecomment-1524227075
public async IAsyncEnumerable<T> Deserialize<T>(Stream stream, [EnumeratorCancellation] CancellationToken cancellationToken)
Expand All @@ -39,7 +31,8 @@ public async IAsyncEnumerable<T> Deserialize<T>(Stream stream, [EnumeratorCancel
var buffer = result.Buffer;
while (!buffer.IsEmpty && TryParseJson(ref buffer, out var jsonDocument))
{
yield return jsonDocument.Deserialize<T>(_options);
var deserializedObj = jsonDocument.Deserialize(typeof(T), DockerDotnetJsonSerializerContext.Default);
yield return (T) deserializedObj;
}

if (result.IsCompleted)
Expand Down Expand Up @@ -68,23 +61,28 @@ private static bool TryParseJson(ref ReadOnlySequence<byte> buffer, out JsonDocu

public T DeserializeObject<T>(byte[] json)
{
return System.Text.Json.JsonSerializer.Deserialize<T>(json, _options);
var deserializedObj = System.Text.Json.JsonSerializer.Deserialize(json, typeof(T), DockerDotnetJsonSerializerContext.Default);
return (T)deserializedObj;
}

public byte[] SerializeObject<T>(T value)
{
return System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(value, _options);
var jsonString = System.Text.Json.JsonSerializer.Serialize(value, typeof(T), DockerDotnetJsonSerializerContext.Default);
return Encoding.UTF8.GetBytes(jsonString);
}

public JsonContent GetHttpContent<T>(T value)
public HttpContent GetHttpContent<T>(T value)
{
return JsonContent.Create(value, options: _options);
var jsonString = System.Text.Json.JsonSerializer.Serialize(value, typeof(T), DockerDotnetJsonSerializerContext.Default);
HttpContent httpContent = new StringContent(jsonString, Encoding.UTF8, "application/json");
return httpContent;
}

public async Task<T> DeserializeAsync<T>(HttpContent content, CancellationToken token)
{
return await content.ReadFromJsonAsync<T>(_options, token)
.ConfigureAwait(false);
var jsonString = await content.ReadAsStringAsync(token);
var deserializedObj = System.Text.Json.JsonSerializer.Deserialize(jsonString, typeof(T), DockerDotnetJsonSerializerContext.Default);
return (T)deserializedObj;
}
}
}
3 changes: 2 additions & 1 deletion src/Docker.DotNet/MapQueryStringConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ public string[] Convert(object o)
{
Debug.Assert(o != null);

return new[] { System.Text.Json.JsonSerializer.Serialize(o) };
var jsonString = System.Text.Json.JsonSerializer.Serialize(o, o.GetType(), DockerDotnetJsonSerializerContext.Default);
return new[] { jsonString };
}
}
}
3 changes: 0 additions & 3 deletions src/Docker.DotNet/Models/RestartPolicyKind.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@ namespace Docker.DotNet.Models

public enum RestartPolicyKind
{
[EnumMember(Value = "")]
Undefined,

[EnumMember(Value = "no")]
No,

Expand Down
17 changes: 9 additions & 8 deletions src/Docker.DotNet/QueryStringConverterInstanceFactory.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Concurrent;
using Docker.DotNet.Models;

namespace Docker.DotNet
{
Expand All @@ -9,19 +10,19 @@ internal class QueryStringConverterInstanceFactory : IQueryStringConverterInstan

public IQueryStringConverter GetConverterInstance(Type t)
{
return ConverterInstanceRegistry.GetOrAdd(
t,
InitializeConverter);
return ConverterInstanceRegistry.GetOrAdd(t, InitializeConverter);
}

private IQueryStringConverter InitializeConverter(Type t)
{
var instance = Activator.CreateInstance(t) as IQueryStringConverter;
if (instance == null)
return t.Name switch
{
throw new InvalidOperationException($"Could not get instance of {t.FullName}");
}
return instance;
nameof(BoolQueryStringConverter) => new BoolQueryStringConverter(),
nameof(EnumerableQueryStringConverter) => new EnumerableQueryStringConverter(),
nameof(MapQueryStringConverter) => new MapQueryStringConverter(),
nameof(TimeSpanSecondsQueryStringConverter) => new TimeSpanSecondsQueryStringConverter(),
_ => throw new InvalidOperationException($"Could not get instance of {t.FullName}")
};
}
}
}
112 changes: 112 additions & 0 deletions tools/specgen/GenJsonSerializerContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@

// File-based app to generate the JsonSerializerContext
// Generates the DefaultJsonSerializerContext.cs from all classes in the Models folder

#:package [email protected]

using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;

string modelsDirectoryPath = @"../../src/Docker.DotNet/Models";
var modelFiles = Directory.GetFiles(modelsDirectoryPath, "*.cs", SearchOption.AllDirectories);

string endpointDirectoryPath = @"../../src/Docker.DotNet/Endpoints";
var endpointFiles = Directory.GetFiles(endpointDirectoryPath, "*.cs", SearchOption.AllDirectories);

HashSet<string> allBaseTypeDeclarations = [];
HashSet<string> allDynamicDependenciesAttributes = [];
HashSet<string> allPropertyDeclarations = [];
HashSet<string> allDockerApiResponseTypes = [];

foreach (var file in modelFiles)
{
var code = File.ReadAllText(file);
var tree = CSharpSyntaxTree.ParseText(code);
var root = tree.GetCompilationUnitRoot();

var baseTypeDeclarations = root.DescendantNodes().OfType<BaseTypeDeclarationSyntax>();

foreach (var typeDecl in baseTypeDeclarations)
{
var name = typeDecl.Identifier.Text;
allBaseTypeDeclarations.Add($"[JsonSerializable(typeof({name}))]");
allDynamicDependenciesAttributes.Add($"[DynamicDependency(DynamicallyAccessedMemberTypes.PublicProperties, typeof(Docker.DotNet.Models.{name}))]");
}

var propertyDeclarations = root.DescendantNodes().OfType<PropertyDeclarationSyntax>();

foreach (var typeSyntax in propertyDeclarations)
{
var typeString = typeSyntax.Type.ToString();
if (typeString.StartsWith("IDictionary") || typeString.StartsWith("IList"))
{
allPropertyDeclarations.Add($"[JsonSerializable(typeof({typeString}))]");
allPropertyDeclarations.Add($"[JsonSerializable(typeof({typeString.Substring(1)}))]");
}
}
}

foreach (var file in endpointFiles)
{
var code = File.ReadAllText(file);
var tree = CSharpSyntaxTree.ParseText(code);
var root = tree.GetCompilationUnitRoot();

var invocations = root.DescendantNodes().OfType<InvocationExpressionSyntax>();

foreach (var invocation in invocations)
{
if (invocation.Expression is MemberAccessExpressionSyntax memberAccess
&& memberAccess.Name is GenericNameSyntax genericMember
&& genericMember.Identifier.Text.Contains("MakeRequestAsync"))
{
foreach (var typeArg in genericMember.TypeArgumentList.Arguments)
{
allDockerApiResponseTypes.Add($"[JsonSerializable(typeof({typeArg.ToString()}))]");
}
}
}
}

var fileContent =
$$"""
// <autogenerated />

using Docker.DotNet.Models;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Text.Json.Serialization;

using Version = Docker.DotNet.Models.Version;

namespace Docker.DotNet;

{{string.Join(Environment.NewLine, allBaseTypeDeclarations)}}

{{string.Join(Environment.NewLine, allPropertyDeclarations)}}

{{string.Join(Environment.NewLine, allDockerApiResponseTypes)}}

[JsonSourceGenerationOptions(
UseStringEnumConverter = true,
Converters =
[
typeof(JsonEnumMemberConverter<TaskState>),
typeof(JsonEnumMemberConverter<RestartPolicyKind>),
typeof(JsonDateTimeConverter),
typeof(JsonNullableDateTimeConverter),
typeof(JsonBase64Converter)
])]
public partial class DockerDotnetJsonSerializerContext : JsonSerializerContext
{
{{string.Join(Environment.NewLine + " ", allDynamicDependenciesAttributes)}}
internal static void PreserveReflection()
{
}
}

""";


string savePath = @"../../src/Docker.DotNet/DockerDotnetJsonSerializerContext.Generated.cs";
File.WriteAllText(savePath, fileContent);