* Good start

* Misc

* Make ApiAuthenticationMiddleware use new json

* Remove first newtonsoft dependency

* Pull latest ASFB json enhancements

* Start reimplementing newtonsoft!

* One thing at a time

* Keep doing all kind of breaking changes which need to be tested later

* Add back ShouldSerialize() support

* Misc

* Eradicate remaining parts of newtonsoft

* WIP

* Workaround STJ stupidity in regards to derived types

STJ can't serialize derived type properties by default, so we'll use another approach in our serializable file function

* Make CI happy

* Bunch of further fixes

* Fix AddFreeLicense() after rewrite

* Add full support for JsonDisallowNullAttribute

* Optimize our json utilities even further

* Misc

* Add support for fields in disallow null

* Misc optimization

* Fix deserialization of GlobalCache in STD

* Fix non-public [JsonExtensionData]

* Fix IM missing method exception, correct db storage helpers

* Fix saving into generic databases

Thanks STJ

* Make Save() function abstract to force inheritors to implement it properly

* Correct ShouldSerializeAdditionalProperties to be a method

* Misc cleanup

* Code review

* Allow JSON comments in configs, among other

* Allow trailing commas in configs

Users very often add them accidentally, no reason to throw on them

* Fix confirmation ID

Probably needs further fixes, will need to check later

* Correct confirmations deserialization

* Use JsonNumberHandling

* Misc

* Misc

* [JsonDisallowNull] corrections

* Forbid [JsonDisallowNull] on non-nullable structs

* Not really but okay

* Add and use ToJson() helpers

* Misc

* Misc
This commit is contained in:
Łukasz Domeradzki
2024-02-21 03:09:36 +01:00
committed by GitHub
parent 3968130e15
commit 6b0bf0f9c1
114 changed files with 1714 additions and 1212 deletions

View File

@@ -24,6 +24,7 @@ using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Text.Json;
using System.Threading.Tasks;
using ArchiSteamFarm.Core;
using ArchiSteamFarm.IPC.Requests;
@@ -32,7 +33,6 @@ using ArchiSteamFarm.Localization;
using ArchiSteamFarm.Steam.Interaction;
using ArchiSteamFarm.Storage;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json.Linq;
namespace ArchiSteamFarm.IPC.Controllers.Api;
@@ -124,9 +124,9 @@ public sealed class ASFController : ArchiController {
}
if (ASF.GlobalConfig.AdditionalProperties is { Count: > 0 }) {
request.GlobalConfig.AdditionalProperties ??= new Dictionary<string, JToken>(ASF.GlobalConfig.AdditionalProperties.Count, ASF.GlobalConfig.AdditionalProperties.Comparer);
request.GlobalConfig.AdditionalProperties ??= new Dictionary<string, JsonElement>(ASF.GlobalConfig.AdditionalProperties.Count, ASF.GlobalConfig.AdditionalProperties.Comparer);
foreach ((string key, JToken value) in ASF.GlobalConfig.AdditionalProperties.Where(property => !request.GlobalConfig.AdditionalProperties.ContainsKey(property.Key))) {
foreach ((string key, JsonElement value) in ASF.GlobalConfig.AdditionalProperties.Where(property => !request.GlobalConfig.AdditionalProperties.ContainsKey(property.Key))) {
request.GlobalConfig.AdditionalProperties.Add(key, value);
}

View File

@@ -25,6 +25,7 @@ using System.Collections.Specialized;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Text.Json;
using System.Threading.Tasks;
using ArchiSteamFarm.Core;
using ArchiSteamFarm.IPC.Requests;
@@ -33,7 +34,6 @@ using ArchiSteamFarm.Localization;
using ArchiSteamFarm.Steam;
using ArchiSteamFarm.Steam.Storage;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json.Linq;
using SteamKit2.Internal;
namespace ArchiSteamFarm.IPC.Controllers.Api;
@@ -127,9 +127,9 @@ public sealed class BotController : ArchiController {
}
if (bot.BotConfig.AdditionalProperties?.Count > 0) {
request.BotConfig.AdditionalProperties ??= new Dictionary<string, JToken>(bot.BotConfig.AdditionalProperties.Count, bot.BotConfig.AdditionalProperties.Comparer);
request.BotConfig.AdditionalProperties ??= new Dictionary<string, JsonElement>(bot.BotConfig.AdditionalProperties.Count, bot.BotConfig.AdditionalProperties.Comparer);
foreach ((string key, JToken value) in bot.BotConfig.AdditionalProperties.Where(property => !request.BotConfig.AdditionalProperties.ContainsKey(property.Key))) {
foreach ((string key, JsonElement value) in bot.BotConfig.AdditionalProperties.Where(property => !request.BotConfig.AdditionalProperties.ContainsKey(property.Key))) {
request.BotConfig.AdditionalProperties.Add(key, value);
}

View File

@@ -30,13 +30,13 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using ArchiSteamFarm.Core;
using ArchiSteamFarm.Helpers.Json;
using ArchiSteamFarm.IPC.Responses;
using ArchiSteamFarm.Localization;
using ArchiSteamFarm.NLog;
using ArchiSteamFarm.NLog.Targets;
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
namespace ArchiSteamFarm.IPC.Controllers.Api;
@@ -156,7 +156,7 @@ public sealed class NLogController : ArchiController {
return;
}
string json = JsonConvert.SerializeObject(new GenericResponse<string>(newHistoryEntryArgs.Message));
string json = new GenericResponse<string>(newHistoryEntryArgs.Message).ToJsonText();
await Task.WhenAll(ActiveLogWebSockets.Where(static kv => kv.Key.State == WebSocketState.Open).Select(kv => PostLoggedJsonUpdate(kv.Key, json, kv.Value.Semaphore, kv.Value.CancellationToken))).ConfigureAwait(false);
}
@@ -206,7 +206,7 @@ public sealed class NLogController : ArchiController {
return;
}
string response = JsonConvert.SerializeObject(new GenericResponse<string>(loggedMessage));
string response = new GenericResponse<string>(loggedMessage).ToJsonText();
await PostLoggedJsonUpdate(webSocket, response, sendSemaphore, cancellationToken).ConfigureAwait(false);
}

View File

@@ -21,10 +21,10 @@
using System;
using System.Net;
using System.Text.Json;
using ArchiSteamFarm.Core;
using ArchiSteamFarm.IPC.Responses;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json.Linq;
namespace ArchiSteamFarm.IPC.Controllers.Api;
@@ -51,7 +51,7 @@ public sealed class StorageController : ArchiController {
/// Loads entry under specified key from ASF's persistent KeyValue JSON storage.
/// </summary>
[HttpGet]
[ProducesResponseType<GenericResponse<JToken>>((int) HttpStatusCode.OK)]
[ProducesResponseType<GenericResponse<JsonElement?>>((int) HttpStatusCode.OK)]
public ActionResult<GenericResponse> StorageGet(string key) {
ArgumentException.ThrowIfNullOrEmpty(key);
@@ -59,9 +59,9 @@ public sealed class StorageController : ArchiController {
throw new InvalidOperationException(nameof(ASF.GlobalDatabase));
}
JToken? value = ASF.GlobalDatabase.LoadFromJsonStorage(key);
JsonElement value = ASF.GlobalDatabase.LoadFromJsonStorage(key);
return Ok(new GenericResponse<JToken>(true, value));
return Ok(new GenericResponse<JsonElement?>(true, value.ValueKind != JsonValueKind.Undefined ? value : null));
}
/// <summary>
@@ -70,15 +70,18 @@ public sealed class StorageController : ArchiController {
[Consumes("application/json")]
[HttpPost]
[ProducesResponseType<GenericResponse>((int) HttpStatusCode.OK)]
public ActionResult<GenericResponse> StoragePost(string key, [FromBody] JToken value) {
public ActionResult<GenericResponse> StoragePost(string key, [FromBody] JsonElement value) {
ArgumentException.ThrowIfNullOrEmpty(key);
ArgumentNullException.ThrowIfNull(value);
if (value.ValueKind == JsonValueKind.Undefined) {
throw new ArgumentOutOfRangeException(nameof(value));
}
if (ASF.GlobalDatabase == null) {
throw new InvalidOperationException(nameof(ASF.GlobalDatabase));
}
if (value.Type == JTokenType.Null) {
if (value.ValueKind == JsonValueKind.Null) {
ASF.GlobalDatabase.DeleteFromJsonStorage(key);
} else {
ASF.GlobalDatabase.SaveToJsonStorage(key, value);

View File

@@ -26,11 +26,11 @@ using System.Globalization;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Text.Json.Serialization;
using ArchiSteamFarm.Core;
using ArchiSteamFarm.IPC.Responses;
using ArchiSteamFarm.Localization;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
namespace ArchiSteamFarm.IPC.Controllers.Api;
@@ -63,27 +63,35 @@ public sealed class TypeController : ArchiController {
if (targetType.IsClass) {
foreach (FieldInfo field in targetType.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public).Where(static field => !field.IsPrivate)) {
JsonPropertyAttribute? jsonProperty = field.GetCustomAttribute<JsonPropertyAttribute>();
if (jsonProperty != null) {
string? unifiedName = field.FieldType.GetUnifiedName();
if (!string.IsNullOrEmpty(unifiedName)) {
body[jsonProperty.PropertyName ?? field.Name] = unifiedName;
}
if (!field.IsDefined(typeof(JsonIncludeAttribute), false) || field.IsDefined(typeof(JsonExtensionDataAttribute), false)) {
continue;
}
string? unifiedName = field.FieldType.GetUnifiedName();
if (string.IsNullOrEmpty(unifiedName)) {
continue;
}
JsonPropertyNameAttribute? jsonPropertyName = field.GetCustomAttribute<JsonPropertyNameAttribute>();
body[jsonPropertyName?.Name ?? field.Name] = unifiedName;
}
foreach (PropertyInfo property in targetType.GetProperties(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public).Where(static property => property is { CanRead: true, GetMethod.IsPrivate: false })) {
JsonPropertyAttribute? jsonProperty = property.GetCustomAttribute<JsonPropertyAttribute>();
if (jsonProperty != null) {
string? unifiedName = property.PropertyType.GetUnifiedName();
if (!string.IsNullOrEmpty(unifiedName)) {
body[jsonProperty.PropertyName ?? property.Name] = unifiedName;
}
if (!property.IsDefined(typeof(JsonIncludeAttribute), false) || property.IsDefined(typeof(JsonExtensionDataAttribute), false)) {
continue;
}
string? unifiedName = property.PropertyType.GetUnifiedName();
if (string.IsNullOrEmpty(unifiedName)) {
continue;
}
JsonPropertyNameAttribute? jsonPropertyName = property.GetCustomAttribute<JsonPropertyNameAttribute>();
body[jsonPropertyName?.Name ?? property.Name] = unifiedName;
}
} else if (targetType.IsEnum) {
Type enumType = Enum.GetUnderlyingType(targetType);