Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OSOE-795: Upgrade to latest OC preview to test System.Text.Json #239

Merged
merged 38 commits into from
May 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
de6150f
Update NuGet versions to pre-release.
sarahelsaig Feb 22, 2024
473a10c
Merge remote-tracking branch 'origin/dev' into issue/OSOE-795
sarahelsaig Feb 22, 2024
765156a
Prevent package downgrade.
sarahelsaig Feb 22, 2024
c417dc2
Fix obsolete code.
sarahelsaig Feb 22, 2024
59055e5
Fix compilation errors.
sarahelsaig Feb 23, 2024
768d118
Remove "using Newtonsoft.Json"
sarahelsaig Feb 24, 2024
84edd2e
Let GetProperty resolve any JSON path.
sarahelsaig Feb 24, 2024
bfbc676
Add missing namespace.
sarahelsaig Feb 25, 2024
bb837a0
Add ToComparable extension method.
sarahelsaig Feb 25, 2024
8bf42e7
Merge remote-tracking branch 'origin/dev' into issue/OSOE-795
sarahelsaig Mar 1, 2024
0a004fa
Update OC to latest (because of bug fix for WorkflowTypeStep)
sarahelsaig Mar 3, 2024
5edfc73
Add RemoveByImplementation extension method.
sarahelsaig Mar 4, 2024
cee975e
Fix ReplaceDefaultShapeTableManager which was crashing due to keyed s…
sarahelsaig Mar 4, 2024
c6b8648
Fix typo.
sarahelsaig Mar 4, 2024
dfcb4f7
Update OC preview version.
sarahelsaig Mar 7, 2024
e3e33a8
Merge branch 'dev' into issue/OSOE-795
sarahelsaig Mar 10, 2024
a42b8fc
Update OC package
sarahelsaig Mar 13, 2024
37b181f
Fix GetProperty.
sarahelsaig Mar 13, 2024
5373337
Merge remote-tracking branch 'origin/dev' into issue/OSOE-795
sarahelsaig Mar 16, 2024
a88c4e7
Add MvcActionContextExtensions.
sarahelsaig Mar 16, 2024
3cb4f28
Update STJ version to v8.
sarahelsaig Mar 17, 2024
b7259b2
Fix warning SA1311.
sarahelsaig Mar 18, 2024
09ff84a
Replace AdminRouteAttribute with AdminAttribute.
sarahelsaig Mar 18, 2024
c24c127
Merge remote-tracking branch 'origin/dev' into issue/OSOE-795
sarahelsaig Mar 29, 2024
cefd8cb
Use JObject.FromObject instead of JsonSerializer.SerializeToNode wher…
sarahelsaig Apr 2, 2024
0925ffb
Merge remote-tracking branch 'origin/dev' into issue/OSOE-795
sarahelsaig Apr 22, 2024
4d43c0c
Remove leftover Newtonseft JsonConvert.
sarahelsaig Apr 22, 2024
efceb5d
Update OC preview version.
sarahelsaig Apr 23, 2024
1d3f7fd
post update fixuppost update fixup
sarahelsaig Apr 23, 2024
92c53d5
unusing
sarahelsaig Apr 23, 2024
df7967c
Update OC package version.
sarahelsaig Apr 27, 2024
86b0022
merge
sarahelsaig May 4, 2024
fefbeed
Update OC versions
sarahelsaig May 4, 2024
b610869
Post merge and OC update fixup
sarahelsaig May 4, 2024
50522e7
Update OC to the latest preview.
sarahelsaig May 7, 2024
aa51b0a
Update readme and references to OC 1.9.
sarahelsaig May 10, 2024
52c1ea1
Add LocalizedHtmlStringConverter unit test.
sarahelsaig May 10, 2024
79a8f35
spell-check-ignore-line again
sarahelsaig May 10, 2024
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
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using Microsoft.AspNetCore.Html;
using Newtonsoft.Json;
using System.Text.Json;

namespace System;

Expand All @@ -10,6 +10,5 @@ public static class JsonStringExtensions
/// tags in a Razor view.
/// </summary>
public static IHtmlContent JsonHtmlContent(this string htmlString) =>
new HtmlString(JsonConvert.SerializeObject(
htmlString, new JsonSerializerSettings { StringEscapeHandling = StringEscapeHandling.EscapeHtml }));
new HtmlString(JsonSerializer.Serialize(htmlString));
dministro marked this conversation as resolved.
Show resolved Hide resolved
}
Original file line number Diff line number Diff line change
@@ -1,49 +1,41 @@
using Microsoft.AspNetCore.Mvc.Localization;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;

namespace Lombiq.HelpfulLibraries.AspNetCore.Localization;

public class LocalizedHtmlStringConverter : JsonConverter<LocalizedHtmlString>
{
public override void WriteJson(JsonWriter writer, LocalizedHtmlString value, JsonSerializer serializer)
public override void Write(Utf8JsonWriter writer, LocalizedHtmlString value, JsonSerializerOptions options)
{
writer.WriteStartObject();

writer.WritePropertyName(nameof(LocalizedHtmlString.Name));
writer.WriteValue(value.Name);

writer.WritePropertyName(nameof(LocalizedHtmlString.Value));
writer.WriteValue(value.Html());

writer.WritePropertyName(nameof(LocalizedHtmlString.IsResourceNotFound));
writer.WriteValue(value.IsResourceNotFound);
writer.WriteString(nameof(LocalizedHtmlString.Name), value.Name);
writer.WriteString(nameof(LocalizedHtmlString.Value), value.Html());
writer.WriteBoolean(nameof(LocalizedHtmlString.IsResourceNotFound), value.IsResourceNotFound);

writer.WriteEndObject();
}

public override LocalizedHtmlString ReadJson(
JsonReader reader,
Type objectType,
LocalizedHtmlString existingValue,
bool hasExistingValue,
JsonSerializer serializer)
public override LocalizedHtmlString Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var token = JToken.Load(reader);
var token = JsonNode.Parse(ref reader);

if (token.Type == JTokenType.String)
if (token is JsonValue jsonValue)
{
var text = token.Value<string>();
var text = jsonValue.GetValue<string>();
return new LocalizedHtmlString(text, text);
}

if (token is JObject jObject)
if (token is JsonObject jsonObject)
{
var name = jObject.GetMaybe(nameof(LocalizedHtmlString.Name))?.ToObject<string>();
var value = jObject.GetMaybe(nameof(LocalizedHtmlString.Value))?.ToObject<string>() ?? name;
var isResourceNotFound = jObject.GetMaybe(nameof(LocalizedHtmlString.IsResourceNotFound))?.ToObject<bool>();
var dictionary = jsonObject.ToDictionaryIgnoreCase();
var name = dictionary.GetMaybe(nameof(LocalizedHtmlString.Name))?.Deserialize<string>();
var value = dictionary.GetMaybe(nameof(LocalizedHtmlString.Value))?.Deserialize<string>() ?? name;
var isResourceNotFound = dictionary.GetMaybe(nameof(LocalizedHtmlString.IsResourceNotFound))?.Deserialize<bool>();

name ??= value;
if (string.IsNullOrEmpty(name)) throw new InvalidOperationException("Missing name.");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
using Microsoft.AspNetCore.Html;
using Newtonsoft.Json;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.Encodings.Web;
using System.Text.Json;

namespace Microsoft.AspNetCore.Mvc.Localization;

Expand All @@ -15,9 +15,7 @@ public static class LocalizedHtmlStringExtensions
/// </summary>
public static IHtmlContent Json(this LocalizedHtmlString htmlString) =>
htmlString?.Html() is { } html
? new HtmlString(JsonConvert.SerializeObject(
html,
new JsonSerializerSettings { StringEscapeHandling = StringEscapeHandling.EscapeHtml }))
? new HtmlString(JsonSerializer.Serialize(html))
: new HtmlString("null");

/// <summary>
Expand Down Expand Up @@ -47,6 +45,9 @@ public static LocalizedHtmlString Concat(this LocalizedHtmlString first, params
return new LocalizedHtmlString(html, html);
}

/// <summary>
/// Concatenates the <paramref name="items"/> with the provided <paramref name="separator"/> in-between.
/// </summary>
public static LocalizedHtmlString Join(this IHtmlContent separator, params LocalizedHtmlString[] items)
{
if (items.Length == 0) return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,6 @@
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="OrchardCore.Abstractions" Version="1.8.2" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Lombiq.HelpfulLibraries.Common\Lombiq.HelpfulLibraries.Common.csproj" />
</ItemGroup>
Expand Down
2 changes: 1 addition & 1 deletion Lombiq.HelpfulLibraries.Common/Docs/Extensions.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
- `ExpressionExtensions`: Adds `System.Linq.Expressions`. For example, `StripResult()` turns a `Func<T1, T2>` expression into an `Action<T1>` one.
- `HttpContextExtensions`: Some shortcuts for managing cookies.
- `IoExtensions`: Adds extensions for `String.IO` types. For example, `TextWriter.WriteLineInvariant()` writes interpolated string in a culture invariant manner.
- `JsonExtensions`: Adds extensions for `Newtonsoft.Json.Linq` types. For example, `JObject.TryParse<T>(out var result)` attempts to convert the JSON object into a C# object.
- `JsonExtensions`: Adds extensions for `System.Text.Json.Nodes` types. For example, `JsonObject.TryParse<T>(out var result)` attempts to convert the JSON object into a C# object.
- `MemoryCacheExtensions`: Adds extensions for `IMemoryCache` manipulation. For example, `GetOrNew<T>()` type-safely returns the item or creates a new instance.
- `MulticastDelegateExtensions`: Extensions for `MulticastDelegate`s, e.g. to invoke async delegates in a safe fashion.
- `NumberExtensions`: Adds extensions for primitive numeric types. For example, `ToTechnicalString()` converts `int` into culture invariant `string`.
Expand Down
30 changes: 27 additions & 3 deletions Lombiq.HelpfulLibraries.Common/Extensions/JsonExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
namespace Newtonsoft.Json.Linq;
using System.Text.Json.Nodes;

namespace System.Text.Json;

public static class JsonExtensions
{
public static bool TryParse<T>(this JObject jObject, out T result)
/// <summary>
/// Attempts to parse the provided <paramref name="jsonNode"/>. If successful, returns <see langword="true"/> and
/// <paramref name="result"/> contains the serialized object. Otherwise returns <see langword="false"/> and
/// <paramref name="result"/> contains <see langword="default"/> of <typeparamref name="T"/>.
/// </summary>
public static bool TryParse<T>(this JsonNode jsonNode, out T result)
{
try
{
result = jObject.ToObject<T>();
result = jsonNode.Deserialize<T>();
return true;
}
catch
Expand All @@ -15,4 +22,21 @@ public static bool TryParse<T>(this JObject jObject, out T result)
return false;
}
}

/// <summary>
/// Converts the provided <paramref name="node"/> into the appropriate primitive types of <see langword="string"/>,
/// <see langword="decimal"/>, <see langword="bool"/> or <see langword="null"/>. If the <paramref name="node"/> is
/// array or object then it's serialized into JSON <see langword="string"/>.
/// </summary>
public static IComparable ToComparable(this JsonNode node) =>
node.GetValueKind() switch
{
JsonValueKind.String => node.GetValue<string>(),
JsonValueKind.Number => node.GetValue<decimal>(),
JsonValueKind.True => true,
JsonValueKind.False => false,
JsonValueKind.Object => node.ToString(),
JsonValueKind.Array => node.ToString(),
_ => null,
};
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;

namespace Microsoft.Extensions.DependencyInjection;
Expand All @@ -9,16 +10,57 @@ namespace Microsoft.Extensions.DependencyInjection;
public static class ServiceCollectionExtensions
{
/// <summary>
/// Removes implementations of type <typeparamref name="T"/> from an <see cref="IServiceCollection"/> instance.
/// Removes all service descriptors where the <see cref="ServiceDescriptor.ImplementationType"/> is <typeparamref
/// name="T"/>.
/// </summary>
/// <param name="includeKeyed">
/// If <see langword="true"/>, it also considers keyed services by checking the <see
/// cref="ServiceDescriptor.KeyedImplementationType"/>.
/// </param>
public static IServiceCollection RemoveByImplementation<T>(this IServiceCollection services, bool includeKeyed = true) =>
services.RemoveByImplementation(typeof(T), includeKeyed);

/// <summary>
/// Removes all service descriptors where the <see cref="ServiceDescriptor.ImplementationType"/> is <paramref
/// name="implementationType"/>.
/// </summary>
/// <param name="includeKeyed">
/// If <see langword="true"/>, it also considers keyed services by checking the <see
/// cref="ServiceDescriptor.KeyedImplementationType"/>.
/// </param>
public static IServiceCollection RemoveByImplementation(
this IServiceCollection services,
Type implementationType,
bool includeKeyed = true)
{
// Have to check "service.IsKeyedService" to avoid new breaking behavior described here:
// https://github.com/dotnet/runtime/issues/95789
services.RemoveAll(service =>
(includeKeyed || !service.IsKeyedService) &&
(service.IsKeyedService ? service.KeyedImplementationType : service.ImplementationType) == implementationType);

return services;
}

[Obsolete($"Use {nameof(RemoveImplementationsOf)} instead (renamed for clarity).")]
public static IServiceCollection RemoveImplementations<T>(this IServiceCollection services) =>
RemoveImplementations(services, typeof(T).FullName);
services.RemoveImplementationsOf<T>();

[Obsolete($"Use {nameof(RemoveImplementationsOf)} instead (renamed for clarity).")]
public static IServiceCollection RemoveImplementations(this IServiceCollection services, string serviceFullName) =>
services.RemoveImplementationsOf(serviceFullName);

/// <summary>
/// Removes implementations of type <typeparamref name="T"/> from an <see cref="IServiceCollection"/> instance.
/// </summary>
public static IServiceCollection RemoveImplementationsOf<T>(this IServiceCollection services) =>
RemoveImplementationsOf(services, typeof(T).FullName);

/// <summary>
/// Removes the implementations specified in <paramref name="serviceFullName"/> from an
/// <see cref="IServiceCollection"/> instance.
/// </summary>
public static IServiceCollection RemoveImplementations(this IServiceCollection services, string serviceFullName)
public static IServiceCollection RemoveImplementationsOf(this IServiceCollection services, string serviceFullName)
{
var servicesToRemove = services
.Where(service => service.ServiceType?.FullName == serviceFullName)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>

</Project>
14 changes: 14 additions & 0 deletions Lombiq.HelpfulLibraries.Common/Utilities/ICopier.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace Lombiq.HelpfulLibraries.Common.Utilities;

/// <summary>
/// Types implementing this interface can copy their values to a <typeparamref name="TTarget"/> object.
/// </summary>
/// <typeparam name="TTarget">The type of the object to copy to.</typeparam>
public interface ICopier<in TTarget>
{
/// <summary>
/// Copies the applicable contents of the current instance into <paramref name="target"/>, overwriting its original
/// values.
/// </summary>
void CopyTo(TTarget target);
}
Loading