1
0
mirror of https://github.com/bitwarden/server.git synced 2025-06-30 23:52:50 -05:00

Start Migration from Newtonsoft.Json to System.Text.Json (#1803)

* Start switch to System.Text.Json

* Work on switching to System.Text.Json

* Main work on STJ refactor

* Fix build errors

* Run formatting

* Delete unused file

* Use legacy for two factor providers

* Run formatter

* Add TokenProviderTests

* Run formatting

* Fix merge issues

* Switch to use JsonSerializer

* Address PR feedback

* Fix formatting

* Ran formatter

* Switch to async

* Ensure Enums are serialized as strings

* Fix formatting

* Enqueue single items as arrays

* Remove CreateAsync method on AzureQueueService
This commit is contained in:
Justin Baur
2022-01-21 09:36:25 -05:00
committed by GitHub
parent 897a76ff48
commit 5268f2781e
91 changed files with 974 additions and 698 deletions

View File

@ -1,8 +1,8 @@
using System;
using System.Collections.Generic;
using System.Text.Json;
using Bit.Core.Models.Data;
using Bit.Core.Utilities;
using Newtonsoft.Json;
namespace Bit.Core.Entities
{
@ -42,7 +42,7 @@ namespace Bit.Core.Entities
try
{
_attachmentData = JsonConvert.DeserializeObject<Dictionary<string, CipherAttachment.MetaData>>(Attachments);
_attachmentData = JsonSerializer.Deserialize<Dictionary<string, CipherAttachment.MetaData>>(Attachments);
foreach (var kvp in _attachmentData)
{
kvp.Value.AttachmentId = kvp.Key;
@ -65,7 +65,7 @@ namespace Bit.Core.Entities
}
_attachmentData = data;
Attachments = JsonConvert.SerializeObject(_attachmentData);
Attachments = JsonSerializer.Serialize(_attachmentData);
}
public void AddAttachment(string id, CipherAttachment.MetaData data)

View File

@ -2,10 +2,10 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text.Json;
using Bit.Core.Enums;
using Bit.Core.Models;
using Bit.Core.Utilities;
using Newtonsoft.Json;
namespace Bit.Core.Entities
{
@ -142,13 +142,13 @@ namespace Bit.Core.Entities
if (_twoFactorProviders == null)
{
_twoFactorProviders =
JsonConvert.DeserializeObject<Dictionary<TwoFactorProviderType, TwoFactorProvider>>(
JsonSerializer.Deserialize<Dictionary<TwoFactorProviderType, TwoFactorProvider>>(
TwoFactorProviders);
}
return _twoFactorProviders;
}
catch (JsonSerializationException)
catch (JsonException)
{
return null;
}
@ -163,10 +163,7 @@ namespace Bit.Core.Entities
return;
}
TwoFactorProviders = JsonConvert.SerializeObject(providers, new JsonSerializerSettings
{
ContractResolver = new EnumKeyResolver<byte>()
});
TwoFactorProviders = JsonSerializer.Serialize(providers);
_twoFactorProviders = providers;
}

View File

@ -1,11 +1,11 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Text.Json;
using Bit.Core.Enums;
using Bit.Core.Models;
using Bit.Core.Utilities;
using Microsoft.AspNetCore.Identity;
using Newtonsoft.Json;
namespace Bit.Core.Entities
{
@ -108,13 +108,13 @@ namespace Bit.Core.Entities
if (_twoFactorProviders == null)
{
_twoFactorProviders =
JsonConvert.DeserializeObject<Dictionary<TwoFactorProviderType, TwoFactorProvider>>(
JsonHelpers.LegacyDeserialize<Dictionary<TwoFactorProviderType, TwoFactorProvider>>(
TwoFactorProviders);
}
return _twoFactorProviders;
}
catch (JsonSerializationException)
catch (JsonException)
{
return null;
}
@ -132,10 +132,7 @@ namespace Bit.Core.Entities
public void SetTwoFactorProviders(Dictionary<TwoFactorProviderType, TwoFactorProvider> providers)
{
TwoFactorProviders = JsonConvert.SerializeObject(providers, new JsonSerializerSettings
{
ContractResolver = new EnumKeyResolver<byte>()
});
TwoFactorProviders = JsonHelpers.LegacySerialize(providers);
_twoFactorProviders = providers;
}

View File

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using Bit.Core.Entities;
using Bit.Core.Enums;
@ -10,7 +11,6 @@ using Bit.Core.Services;
using Bit.Core.Settings;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using U2F.Core.Exceptions;
using U2F.Core.Models;
using U2F.Core.Utils;
@ -107,8 +107,8 @@ namespace Bit.Core.Identity
});
}
var oldToken = JsonConvert.SerializeObject(oldChallenges);
var token = JsonConvert.SerializeObject(new
var oldToken = JsonSerializer.Serialize(oldChallenges);
var token = JsonSerializer.Serialize(new
{
appId = appId,
challenge = challengeBytes.ByteArrayToBase64String(),

View File

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using Bit.Core.Entities;
using Bit.Core.Enums;
@ -12,7 +13,6 @@ using Fido2NetLib;
using Fido2NetLib.Objects;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
namespace Bit.Core.Identity
{
@ -98,7 +98,7 @@ namespace Bit.Core.Identity
return false;
}
var clientResponse = JsonConvert.DeserializeObject<AuthenticatorAssertionRawResponse>(token);
var clientResponse = JsonSerializer.Deserialize<AuthenticatorAssertionRawResponse>(token);
var jsonOptions = provider.MetaData["login"].ToString();
var options = AssertionOptions.FromJson(jsonOptions);

View File

@ -15,7 +15,7 @@
<tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
<td class="content-block last" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; margin: 0; -webkit-font-smoothing: antialiased; padding: 0; -webkit-text-size-adjust: none; text-align: center;" valign="top" align="center">
If you do not wish to join this organization, you can safely ignore this email.
{{#if OrganizationCanSponsor}}
{{#jsonIf OrganizationCanSponsor}}
<p style="margin-top:10px">
<b
style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
@ -24,7 +24,7 @@
Members of {{OrganizationName}} receive a complimentary Families subscription. Learn more at the
following link: https://bitwarden.com/help/article/families-for-enterprise/
</p>
{{/if}}
{{/jsonIf}}
</td>
</tr>
</table>

View File

@ -6,7 +6,7 @@ You have been invited to join the {{OrganizationName}} organization. To accept t
This link expires on {{ExpirationDate}}.
If you do not wish to join this organization, you can safely ignore this email.
{{#if OrganizationCanSponsor}}
{{#jsonIf OrganizationCanSponsor}}
Did you know? Members of {{OrganizationName}} receive a complimentary Families subscription. Learn more here: https://bitwarden.com/help/article/families-for-enterprise/
{{/if}}
{{/jsonIf}}
{{/BasicTextLayout}}

View File

@ -1,10 +1,10 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Text.Json;
using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Utilities;
using Newtonsoft.Json;
namespace Bit.Core.Models.Api.Request.Accounts
{
@ -43,7 +43,7 @@ namespace Bit.Core.Models.Api.Request.Accounts
if (ReferenceData != null)
{
user.ReferenceData = JsonConvert.SerializeObject(ReferenceData);
user.ReferenceData = JsonSerializer.Serialize(ReferenceData);
}
if (Key != null)

View File

@ -1,27 +1,28 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;
using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Utilities;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
namespace Bit.Billing.Models
{
public class AppleReceiptStatus
{
[JsonProperty("status")]
[JsonPropertyName("status")]
public int? Status { get; set; }
[JsonProperty("environment")]
[JsonPropertyName("environment")]
public string Environment { get; set; }
[JsonProperty("latest_receipt")]
[JsonPropertyName("latest_receipt")]
public string LatestReceipt { get; set; }
[JsonProperty("receipt")]
[JsonPropertyName("receipt")]
public AppleReceipt Receipt { get; set; }
[JsonProperty("latest_receipt_info")]
[JsonPropertyName("latest_receipt_info")]
public List<AppleTransaction> LatestReceiptInfo { get; set; }
[JsonProperty("pending_renewal_info")]
[JsonPropertyName("pending_renewal_info")]
public List<AppleRenewalInfo> PendingRenewalInfo { get; set; }
public string GetOriginalTransactionId()
@ -81,72 +82,59 @@ namespace Bit.Billing.Models
public class AppleReceipt
{
[JsonProperty("receipt_type")]
[JsonPropertyName("receipt_type")]
public string ReceiptType { get; set; }
[JsonProperty("bundle_id")]
[JsonPropertyName("bundle_id")]
public string BundleId { get; set; }
[JsonProperty("receipt_creation_date_ms")]
[JsonPropertyName("receipt_creation_date_ms")]
[JsonConverter(typeof(MsEpochConverter))]
public DateTime ReceiptCreationDate { get; set; }
[JsonProperty("in_app")]
[JsonPropertyName("in_app")]
public List<AppleTransaction> InApp { get; set; }
}
public class AppleRenewalInfo
{
[JsonProperty("expiration_intent")]
[JsonPropertyName("expiration_intent")]
public string ExpirationIntent { get; set; }
[JsonProperty("auto_renew_product_id")]
[JsonPropertyName("auto_renew_product_id")]
public string AutoRenewProductId { get; set; }
[JsonProperty("original_transaction_id")]
[JsonPropertyName("original_transaction_id")]
public string OriginalTransactionId { get; set; }
[JsonProperty("is_in_billing_retry_period")]
[JsonPropertyName("is_in_billing_retry_period")]
public string IsInBillingRetryPeriod { get; set; }
[JsonProperty("product_id")]
[JsonPropertyName("product_id")]
public string ProductId { get; set; }
[JsonProperty("auto_renew_status")]
[JsonPropertyName("auto_renew_status")]
public string AutoRenewStatus { get; set; }
}
public class AppleTransaction
{
[JsonProperty("quantity")]
[JsonPropertyName("quantity")]
public string Quantity { get; set; }
[JsonProperty("product_id")]
[JsonPropertyName("product_id")]
public string ProductId { get; set; }
[JsonProperty("transaction_id")]
[JsonPropertyName("transaction_id")]
public string TransactionId { get; set; }
[JsonProperty("original_transaction_id")]
[JsonPropertyName("original_transaction_id")]
public string OriginalTransactionId { get; set; }
[JsonProperty("purchase_date_ms")]
[JsonPropertyName("purchase_date_ms")]
[JsonConverter(typeof(MsEpochConverter))]
public DateTime PurchaseDate { get; set; }
[JsonProperty("original_purchase_date_ms")]
[JsonPropertyName("original_purchase_date_ms")]
[JsonConverter(typeof(MsEpochConverter))]
public DateTime OriginalPurchaseDate { get; set; }
[JsonProperty("expires_date_ms")]
[JsonPropertyName("expires_date_ms")]
[JsonConverter(typeof(MsEpochConverter))]
public DateTime ExpiresDate { get; set; }
[JsonProperty("cancellation_date_ms")]
[JsonPropertyName("cancellation_date_ms")]
[JsonConverter(typeof(MsEpochConverter))]
public DateTime? CancellationDate { get; set; }
[JsonProperty("web_order_line_item_id")]
[JsonPropertyName("web_order_line_item_id")]
public string WebOrderLineItemId { get; set; }
[JsonProperty("cancellation_reason")]
[JsonPropertyName("cancellation_reason")]
public string CancellationReason { get; set; }
}
public class MsEpochConverter : DateTimeConverterBase
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteRawValue(CoreHelpers.ToEpocMilliseconds((DateTime)value).ToString());
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return CoreHelpers.FromEpocMilliseconds(long.Parse(reader.Value.ToString()));
}
}
}
}

View File

@ -4,11 +4,11 @@ using System.Reflection;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Text.Json.Serialization;
using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Services;
using Bit.Core.Settings;
using Newtonsoft.Json;
namespace Bit.Core.Models.Business
{

View File

@ -1,13 +1,10 @@
using System;
using System.Text.Json.Serialization;
using Bit.Core.Entities;
using Bit.Core.Enums;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
namespace Bit.Core.Models.Business
{
[JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))]
public class ReferenceEvent
{
public ReferenceEvent() { }
@ -23,10 +20,10 @@ namespace Bit.Core.Models.Business
}
}
[JsonConverter(typeof(StringEnumConverter))]
[JsonConverter(typeof(JsonStringEnumConverter))]
public ReferenceEventType Type { get; set; }
[JsonConverter(typeof(StringEnumConverter))]
[JsonConverter(typeof(JsonStringEnumConverter))]
public ReferenceEventSource Source { get; set; }
public Guid Id { get; set; }
@ -52,7 +49,7 @@ namespace Bit.Core.Models.Business
public short? Storage { get; set; }
[JsonConverter(typeof(StringEnumConverter))]
[JsonConverter(typeof(JsonStringEnumConverter))]
public SendType? SendType { get; set; }
public int? MaxAccessCount { get; set; }

View File

@ -1,16 +0,0 @@
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
namespace Bit.Core.Models.Business
{
[JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))]
public class ReferenceEventData
{
public string Id { get; set; }
public string Layout { get; set; }
public string Flow { get; set; }
}
}

View File

@ -4,9 +4,9 @@ using System.Reflection;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Text.Json.Serialization;
using Bit.Core.Entities;
using Bit.Core.Services;
using Newtonsoft.Json;
namespace Bit.Core.Models.Business
{

View File

@ -1,5 +1,5 @@
using System;
using Newtonsoft.Json;
using System.Text.Json.Serialization;
namespace Bit.Core.Models.Data
{
@ -15,21 +15,14 @@ namespace Bit.Core.Models.Data
{
private long _size;
[JsonIgnore]
// We serialize Size as a string since JSON (or Javascript) doesn't support full precision for long numbers
[JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
public long Size
{
get { return _size; }
set { _size = value; }
}
// We serialize Size as a string since JSON (or Javascript) doesn't support full precision for long numbers
[JsonProperty("Size")]
public string SizeString
{
get { return _size.ToString(); }
set { _size = Convert.ToInt64(value); }
}
public string FileName { get; set; }
public string Key { get; set; }

View File

@ -2,7 +2,6 @@
using System.Collections.Generic;
using Bit.Core.Entities;
using Bit.Core.Enums;
using Newtonsoft.Json;
namespace Bit.Core.Models.Data
{

View File

@ -1,7 +1,8 @@
using System;
using System.Collections.Generic;
using System.Text.Json;
using Bit.Core.Enums;
using Newtonsoft.Json;
using Bit.Core.Utilities;
namespace Bit.Core.Models.Data
{
@ -37,13 +38,13 @@ namespace Bit.Core.Models.Data
if (_twoFactorProviders == null)
{
_twoFactorProviders =
JsonConvert.DeserializeObject<Dictionary<TwoFactorProviderType, TwoFactorProvider>>(
JsonHelpers.LegacyDeserialize<Dictionary<TwoFactorProviderType, TwoFactorProvider>>(
TwoFactorProviders);
}
return _twoFactorProviders;
}
catch (JsonSerializationException)
catch (Newtonsoft.Json.JsonException)
{
return null;
}

View File

@ -1,6 +1,6 @@
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using System.Text.Json.Serialization;
namespace Bit.Core.Models.Data
{
@ -25,7 +25,6 @@ namespace Bit.Core.Models.Data
public bool ManageResetPassword { get; set; }
[JsonIgnore]
[System.Text.Json.Serialization.JsonIgnore]
public List<(bool Permission, string ClaimName)> ClaimsMap => new()
{
(AccessEventLogs, "accesseventlogs"),

View File

@ -1,12 +1,10 @@
using System;
using Newtonsoft.Json;
using System.Text.Json.Serialization;
namespace Bit.Core.Models.Data
{
public class SendFileData : SendData
{
private long _size;
public SendFileData() { }
public SendFileData(string name, string notes, string fileName)
@ -15,20 +13,9 @@ namespace Bit.Core.Models.Data
FileName = fileName;
}
[JsonIgnore]
public long Size
{
get { return _size; }
set { _size = value; }
}
// We serialize Size as a string since JSON (or Javascript) doesn't support full precision for long numbers
[JsonProperty("Size")]
public string SizeString
{
get { return _size.ToString(); }
set { _size = Convert.ToInt64(value); }
}
[JsonNumberHandling(JsonNumberHandling.WriteAsString | JsonNumberHandling.AllowReadingFromString)]
public long Size { get; set; }
public string Id { get; set; }
public string FileName { get; set; }

View File

@ -9,6 +9,6 @@ namespace Bit.Core.Models.Mail
IEnumerable<string> BccEmails { get; set; }
string Category { get; set; }
string TemplateName { get; set; }
dynamic Model { get; set; }
object Model { get; set; }
}
}

View File

@ -1,6 +1,6 @@
using System.Collections.Generic;
using System.Text.Json.Serialization;
using Bit.Core.Utilities;
using Newtonsoft.Json;
namespace Bit.Core.Models.Mail
{
@ -11,12 +11,13 @@ namespace Bit.Core.Models.Mail
public IEnumerable<string> BccEmails { get; set; }
public string Category { get; set; }
public string TemplateName { get; set; }
[JsonConverter(typeof(ExpandoObjectJsonConverter))]
public dynamic Model { get; set; }
[JsonConverter(typeof(HandlebarsObjectJsonConverter))]
public object Model { get; set; }
public MailQueueMessage() { }
public MailQueueMessage(MailMessage message, string templateName, dynamic model)
public MailQueueMessage(MailMessage message, string templateName, object model)
{
Subject = message.Subject;
ToEmails = message.ToEmails;

View File

@ -1,10 +1,10 @@
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text.Json.Serialization;
using Bit.Core.Enums;
using Bit.Core.Utilities;
using Fido2NetLib.Objects;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using PeterO.Cbor;
using U2F.Core.Utils;
@ -102,7 +102,7 @@ namespace Bit.Core.Models
catch
{
// Handle newtonsoft parsing
Descriptor = JsonConvert.DeserializeObject<PublicKeyCredentialDescriptor>(o.Descriptor.ToString());
Descriptor = JsonHelpers.LegacyDeserialize<PublicKeyCredentialDescriptor>(o.Descriptor.ToString());
}
PublicKey = o.PublicKey;
UserHandle = o.UserHandle;

View File

@ -2,6 +2,8 @@
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using Bit.Billing.Models;
using Bit.Core.Repositories;
@ -9,8 +11,6 @@ using Bit.Core.Settings;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Bit.Core.Services
{
@ -97,14 +97,16 @@ namespace Bit.Core.Services
}
var url = string.Format("https://{0}.itunes.apple.com/verifyReceipt", prod ? "buy" : "sandbox");
var json = new JObject(new JProperty("receipt-data", receiptData),
new JProperty("password", _globalSettings.AppleIap.Password)).ToString();
var response = await _httpClient.PostAsync(url, new StringContent(json));
var response = await _httpClient.PostAsJsonAsync(url, new AppleVerifyReceiptRequestModel
{
ReceiptData = receiptData,
Password = _globalSettings.AppleIap.Password
});
if (response.IsSuccessStatusCode)
{
var responseJson = await response.Content.ReadAsStringAsync();
var receiptStatus = JsonConvert.DeserializeObject<AppleReceiptStatus>(responseJson);
var receiptStatus = await response.Content.ReadFromJsonAsync<AppleReceiptStatus>();
if (receiptStatus.Status == 21007)
{
return await GetReceiptStatusAsync(receiptData, false, attempt + 1, receiptStatus);
@ -124,4 +126,12 @@ namespace Bit.Core.Services
return null;
}
}
public class AppleVerifyReceiptRequestModel
{
[JsonPropertyName("receipt-data")]
public string ReceiptData { get; set; }
[JsonPropertyName("password")]
public string Password { get; set; }
}
}

View File

@ -5,7 +5,7 @@ using System.Threading.Tasks;
using Azure.Storage.Queues;
using Bit.Core.Models.Data;
using Bit.Core.Settings;
using Newtonsoft.Json;
using Bit.Core.Utilities;
namespace Bit.Core.Services
{
@ -13,7 +13,9 @@ namespace Bit.Core.Services
{
public AzureQueueEventWriteService(GlobalSettings globalSettings) : base(
new QueueClient(globalSettings.Events.ConnectionString, "event"),
new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore })
JsonHelpers.IgnoreWritingNull)
{ }
public Task CreateAsync(IEvent e) => CreateManyAsync(new[] { e });
}
}

View File

@ -5,7 +5,7 @@ using System.Threading.Tasks;
using Azure.Storage.Queues;
using Bit.Core.Models.Mail;
using Bit.Core.Settings;
using Newtonsoft.Json;
using Bit.Core.Utilities;
namespace Bit.Core.Services
{
@ -13,11 +13,11 @@ namespace Bit.Core.Services
{
public AzureQueueMailService(GlobalSettings globalSettings) : base(
new QueueClient(globalSettings.Mail.ConnectionString, "mail"),
new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore })
JsonHelpers.IgnoreWritingNull)
{ }
public Task EnqueueAsync(IMailQueueMessage message, Func<IMailQueueMessage, Task> fallback) =>
CreateAsync(message);
CreateManyAsync(new[] { message });
public Task EnqueueManyAsync(IEnumerable<IMailQueueMessage> messages, Func<IMailQueueMessage, Task> fallback) =>
CreateManyAsync(messages);

View File

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Threading.Tasks;
using Azure.Storage.Queues;
using Bit.Core.Context;
@ -7,8 +8,8 @@ using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Models;
using Bit.Core.Settings;
using Bit.Core.Utilities;
using Microsoft.AspNetCore.Http;
using Newtonsoft.Json;
namespace Bit.Core.Services
{
@ -18,11 +19,6 @@ namespace Bit.Core.Services
private readonly GlobalSettings _globalSettings;
private readonly IHttpContextAccessor _httpContextAccessor;
private JsonSerializerSettings _jsonSettings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore
};
public AzureQueuePushNotificationService(
GlobalSettings globalSettings,
IHttpContextAccessor httpContextAccessor)
@ -170,8 +166,8 @@ namespace Bit.Core.Services
private async Task SendMessageAsync<T>(PushType type, T payload, bool excludeCurrentContext)
{
var contextId = GetContextIdentifier(excludeCurrentContext);
var message = JsonConvert.SerializeObject(new PushNotificationData<T>(type, payload, contextId),
_jsonSettings);
var message = JsonSerializer.Serialize(new PushNotificationData<T>(type, payload, contextId),
JsonHelpers.IgnoreWritingNull);
await _queueClient.SendMessageAsync(message);
}

View File

@ -1,10 +1,11 @@
using System;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using Azure.Storage.Queues;
using Bit.Core.Models.Business;
using Bit.Core.Settings;
using Newtonsoft.Json;
using Bit.Core.Utilities;
namespace Bit.Core.Services
{
@ -14,10 +15,6 @@ namespace Bit.Core.Services
private readonly QueueClient _queueClient;
private readonly GlobalSettings _globalSettings;
private readonly JsonSerializerSettings _jsonSerializerSettings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
};
public AzureQueueReferenceEventService(
GlobalSettings globalSettings)
@ -40,7 +37,7 @@ namespace Bit.Core.Services
}
try
{
var message = JsonConvert.SerializeObject(referenceEvent, _jsonSerializerSettings);
var message = JsonSerializer.Serialize(referenceEvent, JsonHelpers.IgnoreWritingNullAndCamelCase);
// Messages need to be base64 encoded
var encodedMessage = Convert.ToBase64String(Encoding.UTF8.GetBytes(message));
await _queueClient.SendMessageAsync(encodedMessage);

View File

@ -1,29 +1,22 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using Azure.Storage.Queues;
using Bit.Core.Utilities;
using Newtonsoft.Json;
namespace Bit.Core.Services
{
public abstract class AzureQueueService<T>
{
protected QueueClient _queueClient;
protected JsonSerializerSettings _jsonSettings;
protected JsonSerializerOptions _jsonOptions;
protected AzureQueueService(QueueClient queueClient, JsonSerializerSettings jsonSettings)
protected AzureQueueService(QueueClient queueClient, JsonSerializerOptions jsonOptions)
{
_queueClient = queueClient;
_jsonSettings = jsonSettings;
}
public async Task CreateAsync(T message)
{
var json = JsonConvert.SerializeObject(message, _jsonSettings);
var base64 = CoreHelpers.Base64EncodeString(json);
await _queueClient.SendMessageAsync(base64);
_jsonOptions = jsonOptions;
}
public async Task CreateManyAsync(IEnumerable<T> messages)
@ -33,19 +26,13 @@ namespace Bit.Core.Services
return;
}
if (!messages.Skip(1).Any())
{
await CreateAsync(messages.First());
return;
}
foreach (var json in SerializeMany(messages, _jsonSettings))
foreach (var json in SerializeMany(messages, _jsonOptions))
{
await _queueClient.SendMessageAsync(json);
}
}
protected IEnumerable<string> SerializeMany(IEnumerable<T> messages, JsonSerializerSettings jsonSettings)
protected IEnumerable<string> SerializeMany(IEnumerable<T> messages, JsonSerializerOptions jsonOptions)
{
// Calculate Base-64 encoded text with padding
int getBase64Size(int byteCount) => ((4 * byteCount / 3) + 3) & ~3;
@ -69,7 +56,7 @@ namespace Bit.Core.Services
}
var serializedMessages = messages.Select(message =>
JsonConvert.SerializeObject(message, jsonSettings));
JsonSerializer.Serialize(message, jsonOptions));
foreach (var message in serializedMessages)
{

View File

@ -3,23 +3,23 @@ using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using Bit.Core.Utilities;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Bit.Core.Services
{
public abstract class BaseIdentityClientService
public abstract class BaseIdentityClientService : IDisposable
{
private readonly string _identityScope;
private readonly string _identityClientId;
private readonly string _identityClientSecret;
private readonly ILogger<BaseIdentityClientService> _logger;
private dynamic _decodedToken;
private JsonDocument _decodedToken;
private DateTime? _nextAuthAttempt = null;
public BaseIdentityClientService(
@ -127,9 +127,9 @@ namespace Bit.Core.Services
return false;
}
var responseContent = await response.Content.ReadAsStringAsync();
dynamic tokenResponse = JsonConvert.DeserializeObject(responseContent);
AccessToken = (string)tokenResponse.access_token;
using var jsonDocument = await JsonDocument.ParseAsync(await response.Content.ReadAsStreamAsync());
AccessToken = jsonDocument.RootElement.GetProperty("access_token").GetString();
return true;
}
@ -145,8 +145,7 @@ namespace Bit.Core.Services
{
if (requestObject != null)
{
var stringContent = JsonConvert.SerializeObject(requestObject);
Content = new StringContent(stringContent, Encoding.UTF8, "application/json");
Content = JsonContent.Create(requestObject);
}
}
}
@ -154,17 +153,16 @@ namespace Bit.Core.Services
protected bool TokenNeedsRefresh(int minutes = 5)
{
var decoded = DecodeToken();
var exp = decoded?["exp"];
if (exp == null)
if (!decoded.RootElement.TryGetProperty("exp", out var expProp))
{
throw new InvalidOperationException("No exp in token.");
}
var expiration = CoreHelpers.FromEpocSeconds(exp.Value<long>());
var expiration = CoreHelpers.FromEpocSeconds(expProp.GetInt64());
return DateTime.UtcNow.AddMinutes(-1 * minutes) > expiration;
}
protected JObject DecodeToken()
protected JsonDocument DecodeToken()
{
if (_decodedToken != null)
{
@ -188,8 +186,13 @@ namespace Bit.Core.Services
throw new InvalidOperationException($"{nameof(AccessToken)} must have 3 parts");
}
_decodedToken = JObject.Parse(Encoding.UTF8.GetString(decodedBytes, 0, decodedBytes.Length));
_decodedToken = JsonDocument.Parse(decodedBytes);
return _decodedToken;
}
public void Dispose()
{
_decodedToken.Dispose();
}
}
}

View File

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using Bit.Core.Entities;
using Bit.Core.Enums;
@ -12,7 +13,6 @@ using Bit.Core.Repositories;
using Bit.Core.Settings;
using Bit.Core.Utilities;
using Core.Models.Data;
using Newtonsoft.Json;
namespace Bit.Core.Services
{
@ -208,7 +208,7 @@ namespace Bit.Core.Services
UserId = cipher.UserId,
OrganizationId = cipher.OrganizationId,
AttachmentId = attachmentId,
AttachmentData = JsonConvert.SerializeObject(data)
AttachmentData = JsonSerializer.Serialize(data)
});
cipher.AddAttachment(attachmentId, data);
await _pushService.PushSyncCipherUpdateAsync(cipher, null);
@ -241,7 +241,7 @@ namespace Bit.Core.Services
UserId = cipher.UserId,
OrganizationId = cipher.OrganizationId,
AttachmentId = attachmentId,
AttachmentData = JsonConvert.SerializeObject(data)
AttachmentData = JsonSerializer.Serialize(data)
};
await _cipherRepository.UpdateAttachmentAsync(attachment);
@ -312,7 +312,7 @@ namespace Bit.Core.Services
UserId = cipher.UserId,
OrganizationId = cipher.OrganizationId,
AttachmentId = attachmentId,
AttachmentData = JsonConvert.SerializeObject(attachments[attachmentId])
AttachmentData = JsonSerializer.Serialize(attachments[attachmentId])
};
await _cipherRepository.UpdateAttachmentAsync(updatedAttachment);
@ -347,7 +347,7 @@ namespace Bit.Core.Services
UserId = cipher.UserId,
OrganizationId = cipher.OrganizationId,
AttachmentId = attachmentData.AttachmentId,
AttachmentData = JsonConvert.SerializeObject(attachmentData)
AttachmentData = JsonSerializer.Serialize(attachmentData)
};

View File

@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Json;
using System.Text.Json;
using System.Threading.Tasks;
using Bit.Core.Context;
using Bit.Core.Entities;
@ -8,7 +10,6 @@ using Bit.Core.Models.Business.Tokenables;
using Bit.Core.Settings;
using Bit.Core.Tokens;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
namespace Bit.Core.Services
{
@ -77,9 +78,9 @@ namespace Bit.Core.Services
return false;
}
var responseContent = await responseMessage.Content.ReadAsStringAsync();
dynamic jsonResponse = JsonConvert.DeserializeObject(responseContent);
return (bool)jsonResponse.success;
using var jsonDocument = await responseMessage.Content.ReadFromJsonAsync<JsonDocument>();
var root = jsonDocument.RootElement;
return root.GetProperty("success").GetBoolean();
}
public bool RequireCaptchaValidation(ICurrentContext currentContext) =>

View File

@ -4,6 +4,7 @@ using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Text.Json;
using System.Threading.Tasks;
using Bit.Core.Entities;
using Bit.Core.Entities.Provider;
@ -565,6 +566,35 @@ namespace Bit.Core.Services
var clickTrackingText = (clickTrackingOff ? "clicktracking=off" : string.Empty);
writer.WriteSafeString($"<a href=\"{href}\" target=\"_blank\" {clickTrackingText}>{text}</a>");
});
Handlebars.RegisterHelper("jsonIf", (output, options, context, arguments) =>
{
// Special case for JsonElement
if (arguments[0] is JsonElement jsonElement
&& (jsonElement.ValueKind == JsonValueKind.True || jsonElement.ValueKind == JsonValueKind.False))
{
if (jsonElement.GetBoolean())
{
options.Template(output, context);
}
else
{
options.Inverse(output, context);
}
return;
}
// Fallback to normal
if (HandlebarsUtils.IsTruthy(arguments[0]))
{
options.Template(output, context);
}
else
{
options.Inverse(output, context);
}
});
}
public async Task SendEmergencyAccessInviteEmailAsync(EmergencyAccess emergencyAccess, string name, string token)

View File

@ -4,6 +4,7 @@ using System.IO;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using Bit.Core.Entities;
using Bit.Core.Models.Business;
@ -13,7 +14,6 @@ using Bit.Core.Utilities;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
namespace Bit.Core.Services
{
@ -246,7 +246,7 @@ namespace Bit.Core.Services
}
var data = File.ReadAllText(filePath, Encoding.UTF8);
return JsonConvert.DeserializeObject<UserLicense>(data);
return JsonSerializer.Deserialize<UserLicense>(data);
}
private OrganizationLicense ReadOrganizationLicense(Organization organization)
@ -258,7 +258,7 @@ namespace Bit.Core.Services
}
var data = File.ReadAllText(filePath, Encoding.UTF8);
return JsonConvert.DeserializeObject<OrganizationLicense>(data);
return JsonSerializer.Deserialize<OrganizationLicense>(data);
}
}
}

View File

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Bit.Core.Context;
@ -11,7 +12,6 @@ using Bit.Core.Repositories;
using Bit.Core.Settings;
using Microsoft.AspNetCore.Http;
using Microsoft.Azure.NotificationHubs;
using Newtonsoft.Json;
namespace Bit.Core.Services
{
@ -229,7 +229,7 @@ namespace Bit.Core.Services
new Dictionary<string, string>
{
{ "type", ((byte)type).ToString() },
{ "payload", JsonConvert.SerializeObject(payload) }
{ "payload", JsonSerializer.Serialize(payload) }
}, tag);
}

View File

@ -9,7 +9,6 @@ using Bit.Core.Models;
using Bit.Core.Settings;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
namespace Bit.Core.Services
{
@ -18,11 +17,6 @@ namespace Bit.Core.Services
private readonly GlobalSettings _globalSettings;
private readonly IHttpContextAccessor _httpContextAccessor;
private JsonSerializerSettings _jsonSettings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore
};
public NotificationsApiPushNotificationService(
GlobalSettings globalSettings,
IHttpContextAccessor httpContextAccessor,

View File

@ -15,7 +15,6 @@ using Bit.Core.Settings;
using Bit.Core.Utilities;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Stripe;
namespace Bit.Core.Services
@ -727,8 +726,8 @@ namespace Bit.Core.Services
var dir = $"{_globalSettings.LicenseDirectory}/organization";
Directory.CreateDirectory(dir);
System.IO.File.WriteAllText($"{dir}/{organization.Id}.json",
JsonConvert.SerializeObject(license, Formatting.Indented));
using var fs = System.IO.File.OpenWrite(Path.Combine(dir, $"{organization.Id}.json"));
await JsonSerializer.SerializeAsync(fs, license, JsonHelpers.Indented);
return result;
}
@ -900,8 +899,8 @@ namespace Bit.Core.Services
var dir = $"{_globalSettings.LicenseDirectory}/organization";
Directory.CreateDirectory(dir);
System.IO.File.WriteAllText($"{dir}/{organization.Id}.json",
JsonConvert.SerializeObject(license, Formatting.Indented));
using var fs = System.IO.File.OpenWrite(Path.Combine(dir, $"{organization.Id}.json"));
await JsonSerializer.SerializeAsync(fs, license, JsonHelpers.Indented);
organization.Name = license.Name;
organization.BusinessName = license.BusinessName;
@ -1146,10 +1145,7 @@ namespace Bit.Core.Services
if (invite.Permissions != null)
{
orgUser.Permissions = System.Text.Json.JsonSerializer.Serialize(invite.Permissions, new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
});
orgUser.Permissions = JsonSerializer.Serialize(invite.Permissions, JsonHelpers.CamelCase);
}
if (!orgUser.AccessAll && invite.Collections.Any())
@ -1765,7 +1761,7 @@ namespace Bit.Core.Services
// Block the user from withdrawal if auto enrollment is enabled
if (resetPasswordKey == null && resetPasswordPolicy.Data != null)
{
var data = JsonConvert.DeserializeObject<ResetPasswordDataModel>(resetPasswordPolicy.Data);
var data = JsonSerializer.Deserialize<ResetPasswordDataModel>(resetPasswordPolicy.Data);
if (data?.AutoEnrollEnabled ?? false)
{

View File

@ -1,13 +1,12 @@
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text;
using System.Net.Http.Json;
using System.Threading.Tasks;
using Bit.Core.Settings;
using Bit.Core.Utilities;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
namespace Bit.Core.Services
{
@ -76,15 +75,13 @@ namespace Bit.Core.Services
request.tag = string.Concat(request.tag, "-Cat_", message.Category);
}
var reqJson = JsonConvert.SerializeObject(request);
var responseMessage = await httpClient.PostAsync(
var responseMessage = await httpClient.PostAsJsonAsync(
$"https://{_globalSettings.Mail.PostalDomain}/api/v1/send/message",
new StringContent(reqJson, Encoding.UTF8, "application/json"));
request);
if (responseMessage.IsSuccessStatusCode)
{
var json = await responseMessage.Content.ReadAsStringAsync();
var response = JsonConvert.DeserializeObject<PostalResponse>(json);
var response = await responseMessage.Content.ReadFromJsonAsync<PostalResponse>();
if (response.status != "success")
{
_logger.LogError("Postal send status was not successful: {0}, {1}",

View File

@ -1,6 +1,7 @@
using System;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using Bit.Core.Context;
using Bit.Core.Entities;
@ -12,7 +13,6 @@ using Bit.Core.Repositories;
using Bit.Core.Settings;
using Bit.Core.Utilities;
using Microsoft.AspNetCore.Identity;
using Newtonsoft.Json;
namespace Bit.Core.Services
{
@ -104,8 +104,8 @@ namespace Bit.Core.Services
data.Id = fileId;
data.Size = fileLength;
data.Validated = false;
send.Data = JsonConvert.SerializeObject(data,
new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
send.Data = JsonSerializer.Serialize(data,
JsonHelpers.IgnoreWritingNull);
await SaveSendAsync(send);
return await _sendFileStorageService.GetSendFileUploadUrlAsync(send, fileId);
}
@ -129,7 +129,7 @@ namespace Bit.Core.Services
throw new BadRequestException("Not a File Type Send.");
}
var data = JsonConvert.DeserializeObject<SendFileData>(send.Data);
var data = JsonSerializer.Deserialize<SendFileData>(send.Data);
if (data.Validated)
{
@ -146,7 +146,7 @@ namespace Bit.Core.Services
public async Task<bool> ValidateSendFile(Send send)
{
var fileData = JsonConvert.DeserializeObject<SendFileData>(send.Data);
var fileData = JsonSerializer.Deserialize<SendFileData>(send.Data);
var (valid, realSize) = await _sendFileStorageService.ValidateFileAsync(send, fileData.Id, fileData.Size, _fileSizeLeeway);
@ -163,8 +163,8 @@ namespace Bit.Core.Services
fileData.Size = realSize.Value;
}
fileData.Validated = true;
send.Data = JsonConvert.SerializeObject(fileData,
new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
send.Data = JsonSerializer.Serialize(fileData,
JsonHelpers.IgnoreWritingNull);
await SaveSendAsync(send);
return valid;
@ -175,7 +175,7 @@ namespace Bit.Core.Services
await _sendRepository.DeleteAsync(send);
if (send.Type == Enums.SendType.File)
{
var data = JsonConvert.DeserializeObject<SendFileData>(send.Data);
var data = JsonSerializer.Deserialize<SendFileData>(send.Data);
await _sendFileStorageService.DeleteFileAsync(send, data.Id);
}
await _pushService.PushSyncSendDeleteAsync(send);

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Claims;
using System.Text.Json;
using System.Threading.Tasks;
using Bit.Core.Context;
using Bit.Core.Entities;
@ -19,7 +20,6 @@ using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using File = System.IO.File;
using U2fLib = U2F.Core.Crypto.U2F;
@ -983,7 +983,8 @@ namespace Bit.Core.Services
var dir = $"{_globalSettings.LicenseDirectory}/user";
Directory.CreateDirectory(dir);
File.WriteAllText($"{dir}/{user.Id}.json", JsonConvert.SerializeObject(license, Formatting.Indented));
using var fs = File.OpenWrite(Path.Combine(dir, $"{user.Id}.json"));
await JsonSerializer.SerializeAsync(fs, license, JsonHelpers.Indented);
}
else
{
@ -1068,7 +1069,8 @@ namespace Bit.Core.Services
var dir = $"{_globalSettings.LicenseDirectory}/user";
Directory.CreateDirectory(dir);
File.WriteAllText($"{dir}/{user.Id}.json", JsonConvert.SerializeObject(license, Formatting.Indented));
using var fs = File.OpenWrite(Path.Combine(dir, $"{user.Id}.json"));
await JsonSerializer.SerializeAsync(fs, license, JsonHelpers.Indented);
user.Premium = license.Premium;
user.RevisionDate = DateTime.UtcNow;

View File

@ -23,7 +23,6 @@ using Bit.Core.Settings;
using IdentityModel;
using Microsoft.AspNetCore.DataProtection;
using MimeKit;
using Newtonsoft.Json;
namespace Bit.Core.Utilities
{
@ -332,12 +331,12 @@ namespace Bit.Core.Utilities
/// <summary>
/// Creates a clone of the given object through serializing to json and deserializing.
/// This method is subject to the limitations of Newstonsoft. For example, properties with
/// This method is subject to the limitations of System.Text.Json. For example, properties with
/// inaccessible setters will not be set.
/// </summary>
public static T CloneObject<T>(T obj)
{
return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(obj));
return JsonSerializer.Deserialize<T>(JsonSerializer.Serialize(obj));
}
public static bool SettingHasValue(string setting)

View File

@ -7,7 +7,6 @@ using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
namespace Bit.Core.Utilities
{
@ -41,10 +40,8 @@ namespace Bit.Core.Utilities
$"Slow down! Too many requests. Try again in {rule.Period}." : _options.QuotaExceededMessage;
httpContext.Response.Headers["Retry-After"] = retryAfter;
httpContext.Response.StatusCode = _options.HttpStatusCode;
httpContext.Response.ContentType = "application/json";
var errorModel = new ErrorResponseModel { Message = message };
return httpContext.Response.WriteAsync(JsonConvert.SerializeObject(errorModel));
return httpContext.Response.WriteAsJsonAsync(errorModel, cancellationToken: httpContext.RequestAborted);
}
public override void LogBlockedRequest(HttpContext httpContext, ClientRequestIdentity identity,

View File

@ -15,9 +15,9 @@ using System.IO;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using System.Text.RegularExpressions;
using System.Web;
using Newtonsoft.Json;
namespace Bit.Core.Utilities.Duo
{
@ -175,7 +175,7 @@ namespace Bit.Core.Utilities.Duo
var res = ApiCall(method, path, parameters, timeout, out var statusCode);
try
{
var dict = JsonConvert.DeserializeObject<Dictionary<string, object>>(res);
var dict = JsonSerializer.Deserialize<Dictionary<string, object>>(res);
if (dict["stat"] as string == "OK")
{
return dict["response"] as T;

View File

@ -1,21 +0,0 @@
using System;
using Newtonsoft.Json.Serialization;
namespace Bit.Core.Utilities
{
public class EnumKeyResolver<T> : DefaultContractResolver where T : struct
{
protected override JsonDictionaryContract CreateDictionaryContract(Type objectType)
{
var contract = base.CreateDictionaryContract(objectType);
var keyType = contract.DictionaryKeyType;
if (keyType.BaseType == typeof(Enum))
{
contract.DictionaryKeyResolver = propName => ((T)Enum.Parse(keyType, propName)).ToString();
}
return contract;
}
}
}

View File

@ -1,19 +0,0 @@
using System;
using System.Dynamic;
using Newtonsoft.Json;
namespace Bit.Core.Utilities
{
public class ExpandoObjectJsonConverter : JsonConverter
{
public override bool CanWrite => false;
public override bool CanConvert(Type objectType) => true;
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return serializer.Deserialize<ExpandoObject>(reader);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) => throw new NotImplementedException();
}
}

View File

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Bit.Core.Utilities
{
public class HandlebarsObjectJsonConverter : JsonConverter<object>
{
public override bool CanConvert(Type typeToConvert) => true;
public override object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return JsonSerializer.Deserialize<Dictionary<string, object>>(ref reader, options);
}
public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options)
{
JsonSerializer.Serialize(writer, value, options);
}
}
}

View File

@ -0,0 +1,106 @@
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
using NS = Newtonsoft.Json;
namespace Bit.Core.Utilities
{
public static class JsonHelpers
{
public static JsonSerializerOptions Default { get; }
public static JsonSerializerOptions Indented { get; }
public static JsonSerializerOptions IgnoreWritingNull { get; }
public static JsonSerializerOptions CamelCase { get; }
public static JsonSerializerOptions IgnoreWritingNullAndCamelCase { get; }
static JsonHelpers()
{
Default = new JsonSerializerOptions();
Indented = new JsonSerializerOptions
{
WriteIndented = true,
};
IgnoreWritingNull = new JsonSerializerOptions
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
};
CamelCase = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
};
IgnoreWritingNullAndCamelCase = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
};
}
// NOTE: This is built into .NET 6, it SHOULD be removed when we upgrade
public static T ToObject<T>(this JsonElement element, JsonSerializerOptions options = null)
{
return JsonSerializer.Deserialize<T>(element.GetRawText(), options ?? Default);
}
public static T DeserializeOrNew<T>(string json, JsonSerializerOptions options = null)
where T : new()
{
if (string.IsNullOrWhiteSpace(json))
{
return new T();
}
return JsonSerializer.Deserialize<T>(json, options);
}
#region Legacy Newtonsoft.Json usage
private const string LegacyMessage = "Usage of Newtonsoft.Json should be kept to a minimum and will further be removed when we move to .NET 6";
[Obsolete(LegacyMessage)]
public static NS.JsonSerializerSettings LegacyDefault { get; } = new NS.JsonSerializerSettings();
[Obsolete(LegacyMessage)]
public static string LegacySerialize(object value, NS.JsonSerializerSettings settings = null)
{
return NS.JsonConvert.SerializeObject(value, settings ?? LegacyDefault);
}
[Obsolete(LegacyMessage)]
public static T LegacyDeserialize<T>(string value, NS.JsonSerializerSettings settings = null)
{
return NS.JsonConvert.DeserializeObject<T>(value, settings ?? LegacyDefault);
}
#endregion
}
public class MsEpochConverter : JsonConverter<DateTime?>
{
public override DateTime? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.Null)
{
return null;
}
if (!long.TryParse(reader.GetString(), out var milliseconds))
{
return null;
}
return CoreHelpers.FromEpocMilliseconds(milliseconds);
}
public override void Write(Utf8JsonWriter writer, DateTime? value, JsonSerializerOptions options)
{
if (!value.HasValue)
{
writer.WriteNullValue();
}
writer.WriteStringValue(CoreHelpers.ToEpocMilliseconds(value.Value).ToString());
}
}
}