DotNetWorkQueue.Aq.ExpressionJsonSerializer
1.1.0
See the version list below for details.
dotnet add package DotNetWorkQueue.Aq.ExpressionJsonSerializer --version 1.1.0
NuGet\Install-Package DotNetWorkQueue.Aq.ExpressionJsonSerializer -Version 1.1.0
<PackageReference Include="DotNetWorkQueue.Aq.ExpressionJsonSerializer" Version="1.1.0" />
<PackageVersion Include="DotNetWorkQueue.Aq.ExpressionJsonSerializer" Version="1.1.0" />
<PackageReference Include="DotNetWorkQueue.Aq.ExpressionJsonSerializer" />
paket add DotNetWorkQueue.Aq.ExpressionJsonSerializer --version 1.1.0
#r "nuget: DotNetWorkQueue.Aq.ExpressionJsonSerializer, 1.1.0"
#:package DotNetWorkQueue.Aq.ExpressionJsonSerializer@1.1.0
#addin nuget:?package=DotNetWorkQueue.Aq.ExpressionJsonSerializer&version=1.1.0
#tool nuget:?package=DotNetWorkQueue.Aq.ExpressionJsonSerializer&version=1.1.0
DotNetWorkQueue.Aq.ExpressionJsonSerializer
Expression tree serializer/deserializer for Newtonsoft.Json.
Fork of aquilae/expression-json-serializer, created primarily to make the serializer thread-safe — upstream shares mutable dictionaries across threads, which is unusable under a concurrent consumer. Also adds multi-targeting. Published for use by DotNetWorkQueue.
Install
dotnet add package DotNetWorkQueue.Aq.ExpressionJsonSerializer
Supported targets
- .NET 10.0
- .NET 8.0
- .NET Framework 4.8
- .NET Standard 2.0
Usage
var settings = new JsonSerializerSettings();
settings.Converters.Add(new ExpressionJsonConverter(typeof(MyMessage)));
Expression<Func<MyMessage, bool>> expr = m => m.Value > 10;
string json = JsonConvert.SerializeObject(expr, settings);
var restored = JsonConvert.DeserializeObject<Expression<Func<MyMessage, bool>>>(json, settings);
Changes from upstream
- Thread-safe: all internal dictionaries use
ConcurrentDictionary. This is the main reason the fork exists — upstream's shared mutable caches corrupt under concurrent use. - Added net10.0, net8.0, net48, and netstandard2.0 multi-targeting
- Merged partial loop and goto expression support (see limitation below)
- Added NuGet packaging and GitHub Actions CI/publish pipeline
Known limitations
DebugInfoExpression and DynamicExpression are not implemented and throw
NotImplementedException. DebugInfo needs a SymbolDocumentInfo and Dynamic needs a
CallSiteBinder, neither of which is meaningfully serializable.
ListInit and MemberInit expressions do not round-trip on .NET Framework 4.8; they
work on .NET 8 and .NET 10. Tracked in
#8.
Everything else round-trips, including full statement-expression support: blocks, loops with break and continue, goto/label, switch, and try with catch, filter, fault and finally.
Build and CI
| Pipeline | What it does |
|---|---|
.github/workflows/ci.yml |
Builds the solution and runs the test suite on net10.0 / net8.0 (ubuntu) and net48 (windows). Also packs and publishes to NuGet on a v* tag. |
.github/workflows/sonarcloud.yml |
SonarCloud CI-based analysis. Builds, runs the net10.0 tests with coverlet (OpenCover format), and feeds coverage to SonarCloud. Requires the SONAR_TOKEN repository secret. Rule exclusions are documented in docs/sonar-exclusions.md. |
Jenkinsfile |
Jenkins pipeline that fires on every PR (reported as the continuous-integration/jenkins/pr-merge check). Builds and runs the net10.0 / net8.0 test stages on a Docker agent. |
To reproduce the Sonar coverage run locally:
dotnet build Aq.ExpressionJsonSerializer.sln -c Debug
dotnet test Aq.ExpressionJsonSerializer.Tests/Aq.ExpressionJsonSerializer.Tests.csproj \
-f net10.0 -c Debug --no-build \
--collect:"XPlat Code Coverage;Format=opencover"
Publishing a release
Publishing uses NuGet Trusted Publishing over GitHub OIDC — there is no long-lived API key. The publish job exchanges a short-lived OIDC token for a NuGet key valid for one hour, immediately before pushing.
- Push a version tag:
git tag v1.0.0 && git push origin v1.0.0 - GitHub Actions runs build and tests across all targets, then packs and pushes to nuget.org automatically.
Trusted publishing setup
A one-time policy on nuget.org (your username → Trusted Publishing) backs this:
| Field | Value |
|---|---|
| Policy owner | blehnen |
| Repository Owner | blehnen |
| Repository | expression-json-serializer |
| Workflow File | ci.yml (filename only, no path) |
| Environment | (blank — the publish job declares no GitHub environment) |
To confirm the policy resolves without cutting a release, run the CI workflow manually
from the Actions tab. That triggers the verify-trusted-publishing job, which performs the
OIDC exchange and stops — it never pushes a package. Do this after any change to the policy
or to the workflow filename, since the policy is bound to ci.yml by name.
License
MIT
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 is compatible. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETFramework 4.8
- Newtonsoft.Json (>= 13.0.4)
-
.NETStandard 2.0
- Newtonsoft.Json (>= 13.0.4)
-
net10.0
- Newtonsoft.Json (>= 13.0.4)
-
net8.0
- Newtonsoft.Json (>= 13.0.4)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on DotNetWorkQueue.Aq.ExpressionJsonSerializer:
| Package | Downloads |
|---|---|
|
DotNetWorkQueue
Work queue for dot net 8.0 and 10.0. Supports scheduling, delayed processing, prioritized queues, message expiration, retries with configurable back-off and more. |
GitHub repositories (1)
Showing the top 1 popular GitHub repositories that depend on DotNetWorkQueue.Aq.ExpressionJsonSerializer:
| Repository | Stars |
|---|---|
|
blehnen/DotNetWorkQueue
A work queue for dot.net with SQL server, SQLite, LiteDB, Redis and PostGreSQL backends
|
1.1.0
- Implemented LabelExpression, SwitchExpression and TryExpression. Goto/label blocks now round-trip; previously only Expression.Loop with break/continue worked.
- Fixed serialization of unnamed parameters. Reduced nodes (compound assignment, increment/decrement) produce temporaries with no name, which were written as null and could not be read back.
- Fixed a race in label target creation: ContainsKey-then-index on a ConcurrentDictionary could return two different LabelTargets for one name.
- Throw TypeLoadException / MissingMethodException instead of bare System.Exception for unresolvable types and constructors.
- Test coverage raised from 62% to 87%.
Known limitation: ListInit and MemberInit expressions do not round-trip on .NET Framework 4.8. See https://github.com/blehnen/expression-json-serializer/issues/8