Skip to content

Commit

Permalink
Added System.Text.Json serializer project (doesn't work yet). Known i…
Browse files Browse the repository at this point in the history
…ssues:

1. Lacks support for DataContract/DataMember attributes:
dotnet/runtime#29975
dotnet/runtime#30009
2. Deserializes the primitive values as JsonElement instances.
  • Loading branch information
yallie committed Aug 14, 2020
1 parent 40a9621 commit dad7edd
Show file tree
Hide file tree
Showing 10 changed files with 244 additions and 1 deletion.
@@ -0,0 +1,23 @@
using System.Text.Json.Serialization;
using JsonServices.Messages;

namespace JsonServices.Serialization.SystemTextJson.Internal
{
internal class GenericMessage
{
[JsonPropertyName("jsonrpc")]
public string Version { get; set; }

[JsonPropertyName("method")]
public string Name { get; set; }

[JsonPropertyName("error")]
public Error Error { get; set; }

[JsonPropertyName("id")]
public string Id { get; set; }

public bool IsValid => Version == "2.0" &&
(!string.IsNullOrWhiteSpace(Name) || !string.IsNullOrWhiteSpace(Id));
}
}
@@ -0,0 +1,7 @@
namespace JsonServices.Serialization.SystemTextJson.Internal
{
internal interface IRequestMessage
{
object Parameters { get; }
}
}
@@ -0,0 +1,7 @@
namespace JsonServices.Serialization.SystemTextJson.Internal
{
internal interface IResponseMessage
{
object Result { get; }
}
}
@@ -0,0 +1,12 @@
using System.Runtime.Serialization;
using System.Text.Json.Serialization;

namespace JsonServices.Serialization.SystemTextJson.Internal
{
internal class RequestMsg<T> : IRequestMessage
{
[JsonPropertyName("params")]
public T Parameters { get; set; }
object IRequestMessage.Parameters => Parameters;
}
}
@@ -0,0 +1,11 @@
using System.Text.Json.Serialization;

namespace JsonServices.Serialization.SystemTextJson.Internal
{
internal class ResponseMsg<T> : IResponseMessage
{
[JsonPropertyName("result")]
public T Result { get; set; }
object IResponseMessage.Result => Result;
}
}
@@ -0,0 +1,43 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<Title>JsonServices.Serialization.SystemTextJson</Title>
<Version>0.0.0.1</Version>
<Authors>yallie</Authors>
<Copyright>Copyright Alexey Yakovlev 2020. All Rights Reserved.</Copyright>
<Description>C# Message-Based JSON-RPC Client over WebSockets</Description>
<PackageLicenseUrl>https://github.com/yallie/JsonService/blob/master/LICENSE</PackageLicenseUrl>
<PackageProjectUrl>https://github.com/yallie/JsonService</PackageProjectUrl>
<RepositoryUrl>https://github.com/yallie/JsonService</RepositoryUrl>
<PackageTags>websockets json rpc events</PackageTags>
<TargetFrameworks>net461;netstandard2.0</TargetFrameworks>
</PropertyGroup>

<PropertyGroup>
<CodeAnalysisRuleSet>..\JsonServices.ruleset</CodeAnalysisRuleSet>
<RootNamespace>JsonServices.Serialization.SystemTextJson</RootNamespace>
<AssemblyName>JsonServices.Serialization.SystemTextJson</AssemblyName>
</PropertyGroup>

<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<DebugType>Full</DebugType>
<DebugSymbols>True</DebugSymbols>
<BaseOutputPath>..\..\bin</BaseOutputPath>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="System.Text.Json" Version="4.7.2" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\JsonServices.Core\JsonServices.Core.csproj" />
</ItemGroup>

<ItemGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<PackageReference Include="StyleCop.Analyzers" Version="1.1.1-beta.61">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
</ItemGroup>

</Project>
121 changes: 121 additions & 0 deletions src/JsonServices.Serialization.SystemTextJson/Serializer.cs
@@ -0,0 +1,121 @@
using System;
using System.Text.Json;
using JsonServices.Exceptions;
using JsonServices.Messages;
using JsonServices.Serialization.SystemTextJson.Internal;
using JsonServices.Services;

namespace JsonServices.Serialization.SystemTextJson
{
public class Serializer : ISerializer
{
public string Serialize(IMessage message) => JsonSerializer.Serialize(message);

public IMessage Deserialize(string data, IMessageTypeProvider typeProvider, IMessageNameProvider nameProvider)
{
var preview = default(GenericMessage);
try
{
preview = JsonSerializer.Deserialize<GenericMessage>(data);
}
catch
{
// invalid message format
}

if (preview == null || !preview.IsValid)
{
throw new InvalidRequestException(data)
{
MessageId = preview?.Id,
};
}

// detect message name
var name = preview.Name;
var isRequest = name != null;
if (name == null)
{
// server cannot handle a response message
if (nameProvider == null)
{
throw new InvalidRequestException(data)
{
MessageId = preview.Id,
};
}

// invalid request id
name = nameProvider.TryGetMessageName(preview.Id);
if (name == null)
{
throw new InvalidRequestException(name)
{
MessageId = preview.Id,
};
}
}

try
{
// deserialize request or response message
if (isRequest)
{
return DeserializeRequest(data, name, preview.Id, typeProvider);
}

return DeserializeResponse(data, name, preview.Id, preview.Error, typeProvider);
}
catch (JsonServicesException ex)
{
// make sure MessageId is reported
if (ex.MessageId == null)
{
ex.MessageId = preview.Id;
}

throw;
}
catch (Exception ex)
{
throw new InvalidRequestException(data, ex)
{
MessageId = preview.Id,
};
}
}

private RequestMessage DeserializeRequest(string data, string name, string id, IMessageTypeProvider typeProvider)
{
// get the message request type
var type = typeProvider.GetRequestType(name);
var msgType = typeof(RequestMsg<>).MakeGenericType(new[] { type });

// deserialize the strong-typed message
var reqMsg = (IRequestMessage)JsonSerializer.Deserialize(data, msgType);
return new RequestMessage
{
Name = name,
Parameters = reqMsg.Parameters,
Id = id,
};
}

public ResponseMessage DeserializeResponse(string data, string name, string id, Error error, IMessageTypeProvider typeProvider)
{
// pre-deserialize to get the bulk of the message
var type = typeProvider.GetResponseType(name);

// handle void messages
if (type == typeof(void))
{
return ResponseMessage.Create(null, error, id);
}

// deserialize the strong-typed message
var msgType = typeof(ResponseMsg<>).MakeGenericType(new[] { type });
var respMsg = (IResponseMessage)JsonSerializer.Deserialize(data, msgType);
return ResponseMessage.Create(respMsg.Result, error, id);
}
}
}
Expand Up @@ -10,7 +10,7 @@
<PackageProjectUrl>https://github.com/yallie/JsonService</PackageProjectUrl>
<RepositoryUrl>https://github.com/yallie/JsonService</RepositoryUrl>
<PackageTags>websockets json rpc events</PackageTags>
<TargetFrameworks>net46;netcoreapp2.0</TargetFrameworks>
<TargetFrameworks>net461;netcoreapp2.0</TargetFrameworks>
<RootNamespace>JsonServices.Serialization.Tests</RootNamespace>
<AssemblyName>JsonServices.Serialization.Tests</AssemblyName>
</PropertyGroup>
Expand All @@ -32,6 +32,7 @@
<ProjectReference Include="..\JsonServices.Core\JsonServices.Core.csproj" />
<ProjectReference Include="..\JsonServices.Serialization.Newtonsoft\JsonServices.Serialization.Newtonsoft.csproj" />
<ProjectReference Include="..\JsonServices.Serialization.ServiceStack\JsonServices.Serialization.ServiceStack.csproj" />
<ProjectReference Include="..\JsonServices.Serialization.SystemTextJson\JsonServices.Serialization.SystemTextJson.csproj" />
</ItemGroup>

</Project>
@@ -0,0 +1,12 @@
using JsonServices.Serialization;
using JsonServices.Serialization.SystemTextJson;
using NUnit.Framework;

namespace JsonServices.Tests.Serialization
{
[TestFixture, Ignore("Doesn't work yet")]
public class SerializerTestsSystemTextJson : SerializerTestsBase
{
protected override ISerializer Serializer { get; } = new Serializer();
}
}
6 changes: 6 additions & 0 deletions src/JsonServices.sln
Expand Up @@ -35,6 +35,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JsonServices.Auth.SecureRem
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JsonServices.Sample.CoreServer", "..\sample\sample-core-server\JsonServices.Sample.CoreServer.csproj", "{CA33030D-A387-49CD-9329-042F7B464067}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JsonServices.Serialization.SystemTextJson", "JsonServices.Serialization.SystemTextJson\JsonServices.Serialization.SystemTextJson.csproj", "{EE4F39FE-8EFB-4A98-9EFE-953007F4CB74}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -105,6 +107,10 @@ Global
{CA33030D-A387-49CD-9329-042F7B464067}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CA33030D-A387-49CD-9329-042F7B464067}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CA33030D-A387-49CD-9329-042F7B464067}.Release|Any CPU.Build.0 = Release|Any CPU
{EE4F39FE-8EFB-4A98-9EFE-953007F4CB74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EE4F39FE-8EFB-4A98-9EFE-953007F4CB74}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EE4F39FE-8EFB-4A98-9EFE-953007F4CB74}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EE4F39FE-8EFB-4A98-9EFE-953007F4CB74}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down

0 comments on commit dad7edd

Please sign in to comment.