diff --git a/src/NuGet.Core/NuGet.Protocol/Converters/MetadataFieldStjConverter.cs b/src/NuGet.Core/NuGet.Protocol/Converters/MetadataFieldStjConverter.cs
new file mode 100644
index 00000000000..71d5623c956
--- /dev/null
+++ b/src/NuGet.Core/NuGet.Protocol/Converters/MetadataFieldStjConverter.cs
@@ -0,0 +1,46 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace NuGet.Protocol.Converters
+{
+ ///
+ /// Reads a JSON string or array of strings into a single comma-separated string.
+ /// Equivalent to for System.Text.Json.
+ ///
+ /// NSJ equivalent: .
+ internal sealed class MetadataFieldStjConverter : JsonConverter
+ {
+ public override bool HandleNull => true;
+ public override string Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ if (reader.TokenType == JsonTokenType.Null)
+ {
+ return string.Empty;
+ }
+
+ if (reader.TokenType == JsonTokenType.StartArray)
+ {
+ var values = new List();
+ while (reader.Read() && reader.TokenType != JsonTokenType.EndArray)
+ {
+ var s = reader.GetString();
+ if (!string.IsNullOrWhiteSpace(s))
+ {
+ values.Add(s!);
+ }
+ }
+ return string.Join(", ", values);
+ }
+
+ return reader.GetString() ?? string.Empty;
+ }
+
+ public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options)
+ => throw new NotSupportedException();
+ }
+}
diff --git a/src/NuGet.Core/NuGet.Protocol/Converters/MetadataStringOrArrayStjConverter.cs b/src/NuGet.Core/NuGet.Protocol/Converters/MetadataStringOrArrayStjConverter.cs
new file mode 100644
index 00000000000..b29994264ca
--- /dev/null
+++ b/src/NuGet.Core/NuGet.Protocol/Converters/MetadataStringOrArrayStjConverter.cs
@@ -0,0 +1,42 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace NuGet.Protocol.Converters
+{
+ ///
+ /// Reads a JSON string or array of strings into an of strings.
+ /// Equivalent to for System.Text.Json.
+ ///
+ /// NSJ equivalent: .
+ internal sealed class MetadataStringOrArrayStjConverter : JsonConverter>
+ {
+ public override IReadOnlyList? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ if (reader.TokenType == JsonTokenType.String)
+ {
+ var str = reader.GetString();
+ return string.IsNullOrWhiteSpace(str) ? null : new[] { str! };
+ }
+
+ if (reader.TokenType != JsonTokenType.StartArray)
+ {
+ throw new JsonException(string.Format(System.Globalization.CultureInfo.CurrentCulture, Strings.Error_UnexpectedJsonToken, reader.TokenType));
+ }
+
+ var values = new List();
+ while (reader.Read() && reader.TokenType != JsonTokenType.EndArray)
+ {
+ values.Add(reader.GetString() ?? string.Empty);
+ }
+ return values.ToArray();
+ }
+
+ public override void Write(Utf8JsonWriter writer, IReadOnlyList value, JsonSerializerOptions options)
+ => throw new NotSupportedException();
+ }
+}
diff --git a/src/NuGet.Core/NuGet.Protocol/Converters/NuGetFrameworkStjConverter.cs b/src/NuGet.Core/NuGet.Protocol/Converters/NuGetFrameworkStjConverter.cs
new file mode 100644
index 00000000000..c9a1e3941a7
--- /dev/null
+++ b/src/NuGet.Core/NuGet.Protocol/Converters/NuGetFrameworkStjConverter.cs
@@ -0,0 +1,29 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+using NuGet.Frameworks;
+
+namespace NuGet.Protocol.Converters
+{
+ /// NSJ equivalent: (registered globally in ).
+ internal sealed class NuGetFrameworkStjConverter : JsonConverter
+ {
+ public override bool HandleNull => true;
+ public override NuGetFramework Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ if (reader.TokenType == JsonTokenType.Null)
+ {
+ return NuGetFramework.AnyFramework;
+ }
+
+ var value = reader.GetString();
+ return string.IsNullOrEmpty(value) ? NuGetFramework.AnyFramework : NuGetFramework.Parse(value!);
+ }
+
+ public override void Write(Utf8JsonWriter writer, NuGetFramework value, JsonSerializerOptions options)
+ => writer.WriteStringValue(value.GetShortFolderName());
+ }
+}
diff --git a/src/NuGet.Core/NuGet.Protocol/Converters/NuGetVersionStjConverter.cs b/src/NuGet.Core/NuGet.Protocol/Converters/NuGetVersionStjConverter.cs
new file mode 100644
index 00000000000..3d25db52814
--- /dev/null
+++ b/src/NuGet.Core/NuGet.Protocol/Converters/NuGetVersionStjConverter.cs
@@ -0,0 +1,32 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+using NuGet.Versioning;
+
+namespace NuGet.Protocol.Converters
+{
+ /// NSJ equivalent: (registered globally in ).
+ internal sealed class NuGetVersionStjConverter : JsonConverter
+ {
+ public override NuGetVersion? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ var str = reader.GetString();
+ return str is null ? null : NuGetVersion.Parse(str);
+ }
+
+ public override void Write(Utf8JsonWriter writer, NuGetVersion value, JsonSerializerOptions options)
+ {
+ if (value is null)
+ {
+ writer.WriteNullValue();
+ }
+ else
+ {
+ writer.WriteStringValue(value.ToString());
+ }
+ }
+ }
+}
diff --git a/src/NuGet.Core/NuGet.Protocol/Converters/PackageDependencyGroupStjConverter.cs b/src/NuGet.Core/NuGet.Protocol/Converters/PackageDependencyGroupStjConverter.cs
new file mode 100644
index 00000000000..0e4120937a7
--- /dev/null
+++ b/src/NuGet.Core/NuGet.Protocol/Converters/PackageDependencyGroupStjConverter.cs
@@ -0,0 +1,83 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+using NuGet.Frameworks;
+using NuGet.Packaging;
+using NuGet.Packaging.Core;
+
+namespace NuGet.Protocol.Converters
+{
+ /// No NSJ equivalent.
+ internal sealed class PackageDependencyGroupStjConverter : JsonConverter
+ {
+ private static readonly PackageDependencyStjConverter _dependencyConverter = new();
+
+ public override PackageDependencyGroup Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ if (reader.TokenType != JsonTokenType.StartObject)
+ {
+ throw new JsonException();
+ }
+
+ NuGetFramework? targetFramework = null;
+ var packages = new List();
+
+ while (reader.Read() && reader.TokenType != JsonTokenType.EndObject)
+ {
+ if (reader.TokenType != JsonTokenType.PropertyName)
+ {
+ continue;
+ }
+
+ var propName = reader.GetString();
+ reader.Read();
+
+ if (string.Equals(propName, JsonProperties.TargetFramework, StringComparison.OrdinalIgnoreCase))
+ {
+ if (reader.TokenType != JsonTokenType.Null)
+ {
+ var fw = reader.GetString();
+ targetFramework = string.IsNullOrEmpty(fw) ? null : NuGetFramework.Parse(fw!);
+ }
+ }
+ else if (string.Equals(propName, JsonProperties.Dependencies, StringComparison.OrdinalIgnoreCase))
+ {
+ if (reader.TokenType == JsonTokenType.StartArray)
+ {
+ while (reader.Read() && reader.TokenType != JsonTokenType.EndArray)
+ {
+ packages.Add(_dependencyConverter.Read(ref reader, typeof(PackageDependency), options));
+ }
+ }
+ else
+ {
+ reader.Skip();
+ }
+ }
+ else
+ {
+ reader.Skip();
+ }
+ }
+
+ return new PackageDependencyGroup(targetFramework ?? NuGetFramework.AnyFramework, packages);
+ }
+
+ public override void Write(Utf8JsonWriter writer, PackageDependencyGroup value, JsonSerializerOptions options)
+ {
+ writer.WriteStartObject();
+ writer.WriteString(JsonProperties.TargetFramework, value.TargetFramework.GetShortFolderName());
+ writer.WriteStartArray(JsonProperties.Dependencies);
+ foreach (var pkg in value.Packages)
+ {
+ _dependencyConverter.Write(writer, pkg, options);
+ }
+ writer.WriteEndArray();
+ writer.WriteEndObject();
+ }
+ }
+}
diff --git a/src/NuGet.Core/NuGet.Protocol/Converters/PackageDependencyStjConverter.cs b/src/NuGet.Core/NuGet.Protocol/Converters/PackageDependencyStjConverter.cs
new file mode 100644
index 00000000000..0263fe8c9ea
--- /dev/null
+++ b/src/NuGet.Core/NuGet.Protocol/Converters/PackageDependencyStjConverter.cs
@@ -0,0 +1,72 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Globalization;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+using NuGet.Packaging.Core;
+using NuGet.Versioning;
+
+namespace NuGet.Protocol.Converters
+{
+ /// No NSJ equivalent.
+ internal sealed class PackageDependencyStjConverter : JsonConverter
+ {
+ private static readonly VersionRangeStjConverter VersionRangeConverter = new();
+
+ public override PackageDependency Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ if (reader.TokenType != JsonTokenType.StartObject)
+ {
+ throw new JsonException();
+ }
+
+ string? id = null;
+ VersionRange? range = null;
+
+ while (reader.Read() && reader.TokenType != JsonTokenType.EndObject)
+ {
+ if (reader.TokenType != JsonTokenType.PropertyName)
+ {
+ continue;
+ }
+
+ var propName = reader.GetString();
+ reader.Read();
+
+ if (string.Equals(propName, JsonProperties.PackageId, StringComparison.OrdinalIgnoreCase))
+ {
+ id = reader.GetString();
+ }
+ else if (string.Equals(propName, JsonProperties.Range, StringComparison.OrdinalIgnoreCase))
+ {
+ if (reader.TokenType != JsonTokenType.Null)
+ {
+ range = VersionRangeConverter.Read(ref reader, typeof(VersionRange), options);
+ }
+ }
+ else
+ {
+ reader.Skip();
+ }
+ }
+
+ if (string.IsNullOrEmpty(id))
+ {
+ throw new JsonException(string.Format(CultureInfo.CurrentCulture, Strings.Error_RequiredJsonPropertyMissing, JsonProperties.PackageId));
+ }
+
+ return new PackageDependency(id!, range);
+ }
+
+ public override void Write(Utf8JsonWriter writer, PackageDependency value, JsonSerializerOptions options)
+ {
+ writer.WriteStartObject();
+ writer.WriteString(JsonProperties.PackageId, value.Id);
+ writer.WritePropertyName(JsonProperties.Range);
+ VersionRangeConverter.Write(writer, value.VersionRange, options);
+ writer.WriteEndObject();
+ }
+ }
+}
diff --git a/src/NuGet.Core/NuGet.Protocol/Converters/SafeBoolStjConverter.cs b/src/NuGet.Core/NuGet.Protocol/Converters/SafeBoolStjConverter.cs
new file mode 100644
index 00000000000..8fb3592ebbb
--- /dev/null
+++ b/src/NuGet.Core/NuGet.Protocol/Converters/SafeBoolStjConverter.cs
@@ -0,0 +1,37 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace NuGet.Protocol.Converters
+{
+ /// NSJ equivalent: .
+ internal sealed class SafeBoolStjConverter : JsonConverter
+ {
+ public override bool Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ switch (reader.TokenType)
+ {
+ case JsonTokenType.True:
+ return true;
+ case JsonTokenType.False:
+ case JsonTokenType.Null:
+ return false;
+ case JsonTokenType.String:
+ return bool.TryParse(reader.GetString()?.Trim(), out bool flag) && flag;
+ case JsonTokenType.Number:
+ return reader.TryGetInt64(out long l) && l == 1;
+ default:
+ reader.Skip();
+ return false;
+ }
+ }
+
+ public override void Write(Utf8JsonWriter writer, bool value, JsonSerializerOptions options)
+ {
+ throw new NotSupportedException();
+ }
+ }
+}
diff --git a/src/NuGet.Core/NuGet.Protocol/Converters/SafeUriStjConverter.cs b/src/NuGet.Core/NuGet.Protocol/Converters/SafeUriStjConverter.cs
new file mode 100644
index 00000000000..5b609f4ea45
--- /dev/null
+++ b/src/NuGet.Core/NuGet.Protocol/Converters/SafeUriStjConverter.cs
@@ -0,0 +1,30 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace NuGet.Protocol.Converters
+{
+ /// NSJ equivalent: .
+ internal sealed class SafeUriStjConverter : JsonConverter
+ {
+ public override Uri? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ if (reader.TokenType == JsonTokenType.String)
+ {
+ Uri.TryCreate(reader.GetString()?.Trim(), UriKind.Absolute, out Uri? uri);
+ return uri;
+ }
+
+ reader.Skip();
+ return null;
+ }
+
+ public override void Write(Utf8JsonWriter writer, Uri value, JsonSerializerOptions options)
+ {
+ throw new NotSupportedException();
+ }
+ }
+}
diff --git a/src/NuGet.Core/NuGet.Protocol/Converters/VersionInfoStjConverter.cs b/src/NuGet.Core/NuGet.Protocol/Converters/VersionInfoStjConverter.cs
new file mode 100644
index 00000000000..794af437c21
--- /dev/null
+++ b/src/NuGet.Core/NuGet.Protocol/Converters/VersionInfoStjConverter.cs
@@ -0,0 +1,63 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Globalization;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+using NuGet.Protocol.Core.Types;
+using NuGet.Versioning;
+
+namespace NuGet.Protocol.Converters
+{
+ /// NSJ equivalent: (registered globally in ).
+ internal sealed class VersionInfoStjConverter : JsonConverter
+ {
+ public override VersionInfo Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ if (reader.TokenType != JsonTokenType.StartObject)
+ {
+ throw new JsonException();
+ }
+
+ string? version = null;
+ long? downloads = null;
+
+ while (reader.Read() && reader.TokenType != JsonTokenType.EndObject)
+ {
+ if (reader.TokenType != JsonTokenType.PropertyName)
+ {
+ continue;
+ }
+
+ var propName = reader.GetString();
+ reader.Read();
+
+ if (string.Equals(propName, JsonProperties.Version, StringComparison.OrdinalIgnoreCase))
+ {
+ version = reader.GetString();
+ }
+ else if (string.Equals(propName, "downloads", StringComparison.OrdinalIgnoreCase))
+ {
+ downloads = reader.TokenType == JsonTokenType.Null ? null : reader.GetInt64();
+ }
+ else
+ {
+ reader.Skip();
+ }
+ }
+
+ if (string.IsNullOrEmpty(version))
+ {
+ throw new JsonException(string.Format(CultureInfo.CurrentCulture, Strings.Error_RequiredJsonPropertyMissing, JsonProperties.Version));
+ }
+
+ return new VersionInfo(NuGetVersion.Parse(version!), downloads);
+ }
+
+ public override void Write(Utf8JsonWriter writer, VersionInfo value, JsonSerializerOptions options)
+ {
+ throw new NotSupportedException();
+ }
+ }
+}
diff --git a/src/NuGet.Core/NuGet.Protocol/Strings.Designer.cs b/src/NuGet.Core/NuGet.Protocol/Strings.Designer.cs
index 9a5f163d56b..c1e1099f8db 100644
--- a/src/NuGet.Core/NuGet.Protocol/Strings.Designer.cs
+++ b/src/NuGet.Core/NuGet.Protocol/Strings.Designer.cs
@@ -204,6 +204,24 @@ internal static string Error_PackageIdentityDoesNotMatch {
}
}
+ ///
+ /// Looks up a localized string similar to Required JSON property '{0}' is missing..
+ ///
+ internal static string Error_RequiredJsonPropertyMissing {
+ get {
+ return ResourceManager.GetString("Error_RequiredJsonPropertyMissing", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Unexpected JSON token '{0}'..
+ ///
+ internal static string Error_UnexpectedJsonToken {
+ get {
+ return ResourceManager.GetString("Error_UnexpectedJsonToken", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to The server responded with HTTP '403 Forbidden' when accessing the source '{0}'. This suggests that the server has authenticated your identity but has not permitted you to access the requested resource. Provide credentials that have permissions to view this resource..
///
diff --git a/src/NuGet.Core/NuGet.Protocol/Strings.resx b/src/NuGet.Core/NuGet.Protocol/Strings.resx
index ae0f89c617b..876e01d27e2 100644
--- a/src/NuGet.Core/NuGet.Protocol/Strings.resx
+++ b/src/NuGet.Core/NuGet.Protocol/Strings.resx
@@ -543,4 +543,11 @@ The "s" should be localized to the abbreviation for seconds.
Expected package {0} {1}, but got package {2} {3}0 and 2 are package names, and 1 and 3 are version numbers
+
+ Unexpected JSON token '{0}'.
+
+
+ Required JSON property '{0}' is missing.
+ 0 - property name
+
\ No newline at end of file
diff --git a/src/NuGet.Core/NuGet.Protocol/xlf/Strings.cs.xlf b/src/NuGet.Core/NuGet.Protocol/xlf/Strings.cs.xlf
index 77d074c9b24..e8a628014c1 100644
--- a/src/NuGet.Core/NuGet.Protocol/xlf/Strings.cs.xlf
+++ b/src/NuGet.Core/NuGet.Protocol/xlf/Strings.cs.xlf
@@ -84,6 +84,16 @@
Expected package {0} {1}, but got package {2} {3}0 and 2 are package names, and 1 and 3 are version numbers
+
+ Required JSON property '{0}' is missing.
+ Required JSON property '{0}' is missing.
+ 0 - property name
+
+
+ Unexpected JSON token '{0}'.
+ Unexpected JSON token '{0}'.
+
+ The server responded with HTTP '403 Forbidden' when accessing the source '{0}'. This suggests that the server has authenticated your identity but has not permitted you to access the requested resource. Provide credentials that have permissions to view this resource.Při přístupu ke zdroji {0} server odpověděl zprávou HTTP 403 Zakázáno. To naznačuje, že server ověřil vaši identitu, ale nepovolil vám přístup k požadovanému prostředku. Poskytněte přihlašovací údaje, které mají oprávnění zobrazit tento prostředek.
diff --git a/src/NuGet.Core/NuGet.Protocol/xlf/Strings.de.xlf b/src/NuGet.Core/NuGet.Protocol/xlf/Strings.de.xlf
index 5df404b37ad..bdb8f8bf906 100644
--- a/src/NuGet.Core/NuGet.Protocol/xlf/Strings.de.xlf
+++ b/src/NuGet.Core/NuGet.Protocol/xlf/Strings.de.xlf
@@ -84,6 +84,16 @@
Expected package {0} {1}, but got package {2} {3}0 and 2 are package names, and 1 and 3 are version numbers
+
+ Required JSON property '{0}' is missing.
+ Required JSON property '{0}' is missing.
+ 0 - property name
+
+
+ Unexpected JSON token '{0}'.
+ Unexpected JSON token '{0}'.
+
+ The server responded with HTTP '403 Forbidden' when accessing the source '{0}'. This suggests that the server has authenticated your identity but has not permitted you to access the requested resource. Provide credentials that have permissions to view this resource.Der Server hat beim Zugriff auf die Quelle "{0}" mit dem HTTP-Fehler "403 – Verboten" geantwortet. Dies weist darauf hin, dass der Server Ihre Identität authentifiziert hat, Ihnen aber keinen Zugriff auf die angeforderte Ressource gewährt. Geben Sie Anmeldeinformationen mit Berechtigungen zum Anzeigen dieser Ressource an.
diff --git a/src/NuGet.Core/NuGet.Protocol/xlf/Strings.es.xlf b/src/NuGet.Core/NuGet.Protocol/xlf/Strings.es.xlf
index 91f0c7a1d64..d1dbb63e4f2 100644
--- a/src/NuGet.Core/NuGet.Protocol/xlf/Strings.es.xlf
+++ b/src/NuGet.Core/NuGet.Protocol/xlf/Strings.es.xlf
@@ -84,6 +84,16 @@
Expected package {0} {1}, but got package {2} {3}0 and 2 are package names, and 1 and 3 are version numbers
+
+ Required JSON property '{0}' is missing.
+ Required JSON property '{0}' is missing.
+ 0 - property name
+
+
+ Unexpected JSON token '{0}'.
+ Unexpected JSON token '{0}'.
+
+ The server responded with HTTP '403 Forbidden' when accessing the source '{0}'. This suggests that the server has authenticated your identity but has not permitted you to access the requested resource. Provide credentials that have permissions to view this resource.El servidor respondió con HTTP "403 Prohibido" al acceder al origen "{0}". Este hecho hace pensar que el servidor ha autenticado la identidad pero no le ha permitido acceder al recurso solicitado. Proporcione credenciales que tengan permiso para ver este recurso.
diff --git a/src/NuGet.Core/NuGet.Protocol/xlf/Strings.fr.xlf b/src/NuGet.Core/NuGet.Protocol/xlf/Strings.fr.xlf
index deebaf899b4..884f7850d76 100644
--- a/src/NuGet.Core/NuGet.Protocol/xlf/Strings.fr.xlf
+++ b/src/NuGet.Core/NuGet.Protocol/xlf/Strings.fr.xlf
@@ -84,6 +84,16 @@
Expected package {0} {1}, but got package {2} {3}0 and 2 are package names, and 1 and 3 are version numbers
+
+ Required JSON property '{0}' is missing.
+ Required JSON property '{0}' is missing.
+ 0 - property name
+
+
+ Unexpected JSON token '{0}'.
+ Unexpected JSON token '{0}'.
+
+ The server responded with HTTP '403 Forbidden' when accessing the source '{0}'. This suggests that the server has authenticated your identity but has not permitted you to access the requested resource. Provide credentials that have permissions to view this resource.Le serveur a répondu avec HTTP '403 Interdit' lors de l'accès à la source '{0}'. Le serveur vous a identifié, mais ne vous a pas autorisé à accéder à la ressource demandée. Fournissez des informations d'identification autorisées à afficher cette ressource.
diff --git a/src/NuGet.Core/NuGet.Protocol/xlf/Strings.it.xlf b/src/NuGet.Core/NuGet.Protocol/xlf/Strings.it.xlf
index 4a78c35662e..63e785a0544 100644
--- a/src/NuGet.Core/NuGet.Protocol/xlf/Strings.it.xlf
+++ b/src/NuGet.Core/NuGet.Protocol/xlf/Strings.it.xlf
@@ -84,6 +84,16 @@
Expected package {0} {1}, but got package {2} {3}0 and 2 are package names, and 1 and 3 are version numbers
+
+ Required JSON property '{0}' is missing.
+ Required JSON property '{0}' is missing.
+ 0 - property name
+
+
+ Unexpected JSON token '{0}'.
+ Unexpected JSON token '{0}'.
+
+ The server responded with HTTP '403 Forbidden' when accessing the source '{0}'. This suggests that the server has authenticated your identity but has not permitted you to access the requested resource. Provide credentials that have permissions to view this resource.Il server ha restituito un errore HTTP '403: accesso non consentito' durante l'accesso all'origine '{0}'. Questo suggerisce che il server ha autenticato l'identità dell'utente ma non ha consentito l'accesso alla risorsa richiesta. Specificare credenziali autorizzate a visualizzare questa risorsa.
diff --git a/src/NuGet.Core/NuGet.Protocol/xlf/Strings.ja.xlf b/src/NuGet.Core/NuGet.Protocol/xlf/Strings.ja.xlf
index ea8f32df521..d4cd47de03e 100644
--- a/src/NuGet.Core/NuGet.Protocol/xlf/Strings.ja.xlf
+++ b/src/NuGet.Core/NuGet.Protocol/xlf/Strings.ja.xlf
@@ -84,6 +84,16 @@
Expected package {0} {1}, but got package {2} {3}0 and 2 are package names, and 1 and 3 are version numbers
+
+ Required JSON property '{0}' is missing.
+ Required JSON property '{0}' is missing.
+ 0 - property name
+
+
+ Unexpected JSON token '{0}'.
+ Unexpected JSON token '{0}'.
+
+ The server responded with HTTP '403 Forbidden' when accessing the source '{0}'. This suggests that the server has authenticated your identity but has not permitted you to access the requested resource. Provide credentials that have permissions to view this resource.ソース '{0}' にアクセスするときに、サーバーが HTTP「403 アクセス不可」で応答しました。サーバーが身元を認証したものの、要求されたリソースにアクセスするためのアクセス許可がないことを示します。このリソースを表示するためのアクセス許可を持つ資格情報を提供してください。
diff --git a/src/NuGet.Core/NuGet.Protocol/xlf/Strings.ko.xlf b/src/NuGet.Core/NuGet.Protocol/xlf/Strings.ko.xlf
index a16cb0fd26b..beee5a8fe5c 100644
--- a/src/NuGet.Core/NuGet.Protocol/xlf/Strings.ko.xlf
+++ b/src/NuGet.Core/NuGet.Protocol/xlf/Strings.ko.xlf
@@ -84,6 +84,16 @@
Expected package {0} {1}, but got package {2} {3}0 and 2 are package names, and 1 and 3 are version numbers
+
+ Required JSON property '{0}' is missing.
+ Required JSON property '{0}' is missing.
+ 0 - property name
+
+
+ Unexpected JSON token '{0}'.
+ Unexpected JSON token '{0}'.
+
+ The server responded with HTTP '403 Forbidden' when accessing the source '{0}'. This suggests that the server has authenticated your identity but has not permitted you to access the requested resource. Provide credentials that have permissions to view this resource.'{0}' 원본에 액세스할 때 서버에서 HTTP '403 사용 권한 없음'으로 응답했습니다. 서버에서 ID를 인증했지만 요청된 리소스에 대한 액세스를 허용하지 않았습니다. 이 리소스를 볼 수 있는 권한이 있는 자격 증명을 제공하세요.
diff --git a/src/NuGet.Core/NuGet.Protocol/xlf/Strings.pl.xlf b/src/NuGet.Core/NuGet.Protocol/xlf/Strings.pl.xlf
index bb4da49281f..da9fd54d759 100644
--- a/src/NuGet.Core/NuGet.Protocol/xlf/Strings.pl.xlf
+++ b/src/NuGet.Core/NuGet.Protocol/xlf/Strings.pl.xlf
@@ -84,6 +84,16 @@
Expected package {0} {1}, but got package {2} {3}0 and 2 are package names, and 1 and 3 are version numbers
+
+ Required JSON property '{0}' is missing.
+ Required JSON property '{0}' is missing.
+ 0 - property name
+
+
+ Unexpected JSON token '{0}'.
+ Unexpected JSON token '{0}'.
+
+ The server responded with HTTP '403 Forbidden' when accessing the source '{0}'. This suggests that the server has authenticated your identity but has not permitted you to access the requested resource. Provide credentials that have permissions to view this resource.Serwer wysłał odpowiedź HTTP 403 „Zabronione” podczas uzyskiwania dostępu do źródła „{0}”. Może to oznaczać, że serwer uwierzytelnił Twoją tożsamość, ale nie zezwolił Ci na dostęp do żądanego źródła. Podaj poświadczenia z uprawnieniami do wyświetlania tego zasobu.
diff --git a/src/NuGet.Core/NuGet.Protocol/xlf/Strings.pt-BR.xlf b/src/NuGet.Core/NuGet.Protocol/xlf/Strings.pt-BR.xlf
index 1a551d15316..d5ad92de086 100644
--- a/src/NuGet.Core/NuGet.Protocol/xlf/Strings.pt-BR.xlf
+++ b/src/NuGet.Core/NuGet.Protocol/xlf/Strings.pt-BR.xlf
@@ -84,6 +84,16 @@
Expected package {0} {1}, but got package {2} {3}0 and 2 are package names, and 1 and 3 are version numbers
+
+ Required JSON property '{0}' is missing.
+ Required JSON property '{0}' is missing.
+ 0 - property name
+
+
+ Unexpected JSON token '{0}'.
+ Unexpected JSON token '{0}'.
+
+ The server responded with HTTP '403 Forbidden' when accessing the source '{0}'. This suggests that the server has authenticated your identity but has not permitted you to access the requested resource. Provide credentials that have permissions to view this resource.O servidor respondeu com HTTP '403 Proibido' ao acessar a origem '{0}'. Isso sugere que o servidor autenticou sua identidade, mas não permitiu que você acessasse o recurso solicitado. Forneça credenciais que tenham permissões para exibir esse recurso.
diff --git a/src/NuGet.Core/NuGet.Protocol/xlf/Strings.ru.xlf b/src/NuGet.Core/NuGet.Protocol/xlf/Strings.ru.xlf
index 9843950a1bc..de5e9ff7223 100644
--- a/src/NuGet.Core/NuGet.Protocol/xlf/Strings.ru.xlf
+++ b/src/NuGet.Core/NuGet.Protocol/xlf/Strings.ru.xlf
@@ -84,6 +84,16 @@
Expected package {0} {1}, but got package {2} {3}0 and 2 are package names, and 1 and 3 are version numbers
+
+ Required JSON property '{0}' is missing.
+ Required JSON property '{0}' is missing.
+ 0 - property name
+
+
+ Unexpected JSON token '{0}'.
+ Unexpected JSON token '{0}'.
+
+ The server responded with HTTP '403 Forbidden' when accessing the source '{0}'. This suggests that the server has authenticated your identity but has not permitted you to access the requested resource. Provide credentials that have permissions to view this resource.При попытке обратиться к источнику "{0}" сервер вернул сообщение "HTTP 403 — запрещено". Это значит, что сервер проверил подлинность вашего удостоверения, но запретил доступ к запрошенному ресурсу. Укажите учетные данные, которым разрешено просматривать этот ресурс.
diff --git a/src/NuGet.Core/NuGet.Protocol/xlf/Strings.tr.xlf b/src/NuGet.Core/NuGet.Protocol/xlf/Strings.tr.xlf
index 62db2d704e5..a53679a5473 100644
--- a/src/NuGet.Core/NuGet.Protocol/xlf/Strings.tr.xlf
+++ b/src/NuGet.Core/NuGet.Protocol/xlf/Strings.tr.xlf
@@ -84,6 +84,16 @@
Expected package {0} {1}, but got package {2} {3}0 and 2 are package names, and 1 and 3 are version numbers
+
+ Required JSON property '{0}' is missing.
+ Required JSON property '{0}' is missing.
+ 0 - property name
+
+
+ Unexpected JSON token '{0}'.
+ Unexpected JSON token '{0}'.
+
+ The server responded with HTTP '403 Forbidden' when accessing the source '{0}'. This suggests that the server has authenticated your identity but has not permitted you to access the requested resource. Provide credentials that have permissions to view this resource.'{0}' kaynağına erişilirken sunucu HTTP '403 Yasak' yanıtı döndürdü. Bu, sunucunun kimliğinizi doğruladığı, ancak size istenen kaynağa erişim izni vermediği anlamına gelir. Bu kaynağı görüntüleme iznine sahip kimlik bilgilerini sağlayın.
diff --git a/src/NuGet.Core/NuGet.Protocol/xlf/Strings.zh-Hans.xlf b/src/NuGet.Core/NuGet.Protocol/xlf/Strings.zh-Hans.xlf
index d9883cf789b..8e2a37a7adc 100644
--- a/src/NuGet.Core/NuGet.Protocol/xlf/Strings.zh-Hans.xlf
+++ b/src/NuGet.Core/NuGet.Protocol/xlf/Strings.zh-Hans.xlf
@@ -84,6 +84,16 @@
Expected package {0} {1}, but got package {2} {3}0 and 2 are package names, and 1 and 3 are version numbers
+
+ Required JSON property '{0}' is missing.
+ Required JSON property '{0}' is missing.
+ 0 - property name
+
+
+ Unexpected JSON token '{0}'.
+ Unexpected JSON token '{0}'.
+
+ The server responded with HTTP '403 Forbidden' when accessing the source '{0}'. This suggests that the server has authenticated your identity but has not permitted you to access the requested resource. Provide credentials that have permissions to view this resource.访问源“{0}”时,服务器响应为 HTTP“403 禁止访问”。这表明服务器已验证你的身份,但不允许访问你请求的资源。请提供有权查看此资源的凭据。
diff --git a/src/NuGet.Core/NuGet.Protocol/xlf/Strings.zh-Hant.xlf b/src/NuGet.Core/NuGet.Protocol/xlf/Strings.zh-Hant.xlf
index 8a64b89f22c..5a32e97c555 100644
--- a/src/NuGet.Core/NuGet.Protocol/xlf/Strings.zh-Hant.xlf
+++ b/src/NuGet.Core/NuGet.Protocol/xlf/Strings.zh-Hant.xlf
@@ -84,6 +84,16 @@
Expected package {0} {1}, but got package {2} {3}0 and 2 are package names, and 1 and 3 are version numbers
+
+ Required JSON property '{0}' is missing.
+ Required JSON property '{0}' is missing.
+ 0 - property name
+
+
+ Unexpected JSON token '{0}'.
+ Unexpected JSON token '{0}'.
+
+ The server responded with HTTP '403 Forbidden' when accessing the source '{0}'. This suggests that the server has authenticated your identity but has not permitted you to access the requested resource. Provide credentials that have permissions to view this resource.存取來源 '{0}' 時,伺服器回應了 HTTP '403 禁止'。這表示伺服器已驗證您的身分識別,但未允許您存取要求的資源。請提供有權檢視此資源的認證。
diff --git a/test/NuGet.Core.Tests/NuGet.Protocol.Tests/Converters/StjConverterTests.cs b/test/NuGet.Core.Tests/NuGet.Protocol.Tests/Converters/StjConverterTests.cs
new file mode 100644
index 00000000000..02ee212f09c
--- /dev/null
+++ b/test/NuGet.Core.Tests/NuGet.Protocol.Tests/Converters/StjConverterTests.cs
@@ -0,0 +1,258 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+using FluentAssertions;
+using NuGet.Frameworks;
+using NuGet.Packaging;
+using NuGet.Packaging.Core;
+using NuGet.Protocol.Converters;
+using NuGet.Protocol.Core.Types;
+using NuGet.Versioning;
+using Xunit;
+
+namespace NuGet.Protocol.Tests.Converters
+{
+ public class StjConverterTests
+ {
+ [Theory]
+ [InlineData("true", true)]
+ [InlineData("false", false)]
+ [InlineData("null", false)]
+ [InlineData("1", true)]
+ [InlineData("0", false)]
+ [InlineData("\"true\"", true)]
+ [InlineData("\"True\"", true)]
+ [InlineData("\"false\"", false)]
+ [InlineData("\"invalid\"", false)]
+ [InlineData("\" \"", false)]
+ [InlineData("{}", false)]
+ public void SafeBoolStjConverter_OnVariousInputs_ReturnsCorrectBool(string json, bool expected)
+ {
+ // Act
+ bool actual = Deserialize(json, new SafeBoolStjConverter());
+
+ // Assert
+ actual.Should().Be(expected);
+ }
+
+ [Theory]
+ [InlineData("\"https://contoso.test/path\"", "https://contoso.test/path")]
+ [InlineData("\"not a uri\"", null)]
+ [InlineData("null", null)]
+ [InlineData("{}", null)]
+ public void SafeUriStjConverter_OnVariousInputs_ReturnsCorrectUri(string json, string? expectedUri)
+ {
+ // Act
+ var actual = Deserialize(json, new SafeUriStjConverter());
+
+ // Assert
+ actual?.OriginalString.Should().Be(expectedUri);
+ }
+
+ [Theory]
+ [InlineData("\"1.2.3\"", "1.2.3")]
+ [InlineData("\"1.0.0-beta.1\"", "1.0.0-beta.1")]
+ [InlineData("null", null)]
+ public void NuGetVersionStjConverter_OnVersionString_ReturnsCorrectVersion(string json, string? expectedVersion)
+ {
+ // Act
+ var actual = Deserialize(json, new NuGetVersionStjConverter());
+
+ // Assert
+ actual?.ToString().Should().Be(expectedVersion);
+ }
+
+ [Fact]
+ public void NuGetVersionStjConverter_OnRoundTrip_PreservesVersion()
+ {
+ // Arrange
+ var version = new NuGetVersion(1, 2, 3, "beta.1");
+ var options = OptionsFor(new NuGetVersionStjConverter());
+
+ // Act
+ var json = JsonSerializer.Serialize(version, options);
+ var actual = JsonSerializer.Deserialize(json, options);
+
+ // Assert
+ actual.Should().Be(version);
+ }
+
+ [Theory]
+ [InlineData("\"author\"", "author")]
+ [InlineData("null", "")]
+ [InlineData("[\"Alice\",\"Bob\",\"Charlie\"]", "Alice, Bob, Charlie")]
+ [InlineData("[\"Alice\",\" \",\"\",\"Bob\"]", "Alice, Bob")]
+ public void MetadataFieldStjConverter_OnStringOrArray_ReturnsCorrectJoinedString(string json, string expected)
+ {
+ // Act
+ var actual = Deserialize(json, new MetadataFieldStjConverter());
+
+ // Assert
+ actual.Should().Be(expected);
+ }
+
+ [Theory]
+ [InlineData("\"owner\"", new[] { "owner" })]
+ [InlineData("[\"a\",\"b\",\"c\"]", new[] { "a", "b", "c" })]
+ public void MetadataStringOrArrayStjConverter_OnStringOrArray_ReturnsCorrectItems(string json, string[] expected)
+ {
+ // Act
+ var actual = Deserialize>(json, new MetadataStringOrArrayStjConverter());
+
+ // Assert
+ actual.Should().Equal(expected);
+ }
+
+ [Theory]
+ [InlineData("null")]
+ [InlineData("\" \"")]
+ public void MetadataStringOrArrayStjConverter_OnNullOrWhitespace_ReturnsNull(string json)
+ {
+ // Act
+ var actual = Deserialize>(json, new MetadataStringOrArrayStjConverter());
+
+ // Assert
+ actual.Should().BeNull();
+ }
+
+ [Theory]
+ [InlineData("42")]
+ [InlineData("true")]
+ [InlineData("{}")]
+ public void MetadataStringOrArrayStjConverter_OnUnexpectedTokenType_ThrowsJsonException(string json)
+ {
+ // Act
+ var act = () => Deserialize>(json, new MetadataStringOrArrayStjConverter());
+
+ // Assert
+ act.Should().Throw();
+ }
+
+ [Theory]
+ [InlineData("""{"version":"1.0.0","downloads":12345}""", "1.0.0", 12345L)]
+ [InlineData("""{"version":"2.0.0-beta"}""", "2.0.0-beta", null)]
+ public void VersionInfoStjConverter_OnObject_ReturnsCorrectVersionInfo(string json, string expectedVersion, long? expectedDownloads)
+ {
+ // Act
+ var actual = Deserialize(json, new VersionInfoStjConverter());
+
+ // Assert
+ actual.Version.Should().Be(NuGetVersion.Parse(expectedVersion));
+ actual.DownloadCount.Should().Be(expectedDownloads);
+ }
+
+ [Theory]
+ [InlineData("\"net472\"", "net472")]
+ [InlineData("\"net8.0\"", "net8.0")]
+ [InlineData("null", null)]
+ [InlineData("\"\"", null)]
+ public void NuGetFrameworkStjConverter_OnFrameworkString_ReturnsFramework(string json, string? expectedFramework)
+ {
+ // Arrange
+ var expected = expectedFramework is null ? NuGetFramework.AnyFramework : NuGetFramework.Parse(expectedFramework);
+
+ // Act
+ var actual = Deserialize(json, new NuGetFrameworkStjConverter());
+
+ // Assert
+ actual.Should().Be(expected);
+ }
+
+ [Theory]
+ [InlineData("""{"id":"Newtonsoft.Json","range":"[6.0.0, )"}""", "Newtonsoft.Json", "[6.0.0, )")]
+ [InlineData("""{"id":"SomePackage"}""", "SomePackage", null)]
+ [InlineData("""{"Id":"MyPackage","Range":"[1.0.0, 2.0.0)"}""", "MyPackage", "[1.0.0, 2.0.0)")]
+ public void PackageDependencyStjConverter_OnObject_ReturnsCorrectDependency(string json, string expectedId, string? expectedRange)
+ {
+ // Arrange
+ var expectedVersionRange = expectedRange is null ? VersionRange.All : VersionRange.Parse(expectedRange);
+
+ // Act
+ var actual = Deserialize(json, new PackageDependencyStjConverter());
+
+ // Assert
+ actual.Id.Should().Be(expectedId);
+ actual.VersionRange.Should().Be(expectedVersionRange);
+ }
+
+ [Theory]
+ [InlineData("{}")]
+ [InlineData("""{"range":"[1.0.0, )"}""")]
+ public void PackageDependencyStjConverter_OnMissingId_ThrowsJsonException(string json)
+ {
+ // Act
+ var act = () => Deserialize(json, new PackageDependencyStjConverter());
+
+ // Assert
+ act.Should().Throw();
+ }
+
+ [Fact]
+ public void PackageDependencyStjConverter_OnRoundTrip_PreservesValues()
+ {
+ // Arrange
+ var original = new PackageDependency("Newtonsoft.Json", VersionRange.Parse("[13.0.0, )"));
+ var options = OptionsFor(new PackageDependencyStjConverter());
+
+ // Act
+ var json = JsonSerializer.Serialize(original, options);
+ var actual = JsonSerializer.Deserialize(json, options);
+
+ // Assert
+ actual!.Id.Should().Be(original.Id);
+ actual.VersionRange.Should().Be(original.VersionRange);
+ }
+
+ [Theory]
+ [InlineData("""{"targetFramework":"net8.0","dependencies":[{"id":"Serilog","range":"[3.0.0, )"}]}""", "net8.0", 1)]
+ [InlineData("""{"targetFramework":"net472","dependencies":[]}""", "net472", 0)]
+ [InlineData("""{"targetFramework":null,"dependencies":[]}""", null, 0)]
+ public void PackageDependencyGroupStjConverter_OnObject_ReturnsCorrectGroup(string json, string? expectedFramework, int expectedCount)
+ {
+ // Arrange
+ var expectedTfm = expectedFramework is null ? NuGetFramework.AnyFramework : NuGetFramework.Parse(expectedFramework);
+
+ // Act
+ var actual = Deserialize(json, new PackageDependencyGroupStjConverter());
+
+ // Assert
+ actual.TargetFramework.Should().Be(expectedTfm);
+ actual.Packages.Should().HaveCount(expectedCount);
+ }
+
+ [Fact]
+ public void PackageDependencyGroupStjConverter_OnRoundTrip_PreservesValues()
+ {
+ // Arrange
+ var original = new PackageDependencyGroup(
+ NuGetFramework.Parse("net8.0"),
+ new[] { new PackageDependency("Serilog", VersionRange.Parse("[3.0.0, )")) });
+ var options = OptionsFor(new PackageDependencyGroupStjConverter());
+
+ // Act
+ var json = JsonSerializer.Serialize(original, options);
+ var actual = JsonSerializer.Deserialize(json, options);
+
+ // Assert
+ actual!.TargetFramework.Should().Be(original.TargetFramework);
+ actual.Packages.Should().ContainSingle().Which.Id.Should().Be("Serilog");
+ }
+
+ private static T Deserialize(string json, params JsonConverter[] converters)
+ => JsonSerializer.Deserialize(json, OptionsFor(converters))!;
+
+ private static JsonSerializerOptions OptionsFor(params JsonConverter[] converters)
+ {
+ var options = new JsonSerializerOptions();
+ foreach (var c in converters)
+ {
+ options.Converters.Add(c);
+ }
+ return options;
+ }
+ }
+}