File: JsonPackageSpecReader.Utf8JsonStreamReader.cs
Web Access
Project: src\src\nuget-client\src\NuGet.Core\NuGet.ProjectModel\NuGet.ProjectModel.csproj (NuGet.ProjectModel)
// 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.

#nullable disable

using System;
using System.Buffers;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.Json;
using NuGet.Common;
using NuGet.Configuration;
using NuGet.Frameworks;
using NuGet.LibraryModel;
using NuGet.RuntimeModel;
using NuGet.Shared;
using NuGet.Versioning;

namespace NuGet.ProjectModel
{
    public partial class JsonPackageSpecReader
    {
        private static readonly byte[] DependenciesPropertyName = Encoding.UTF8.GetBytes("dependencies");
        private static readonly byte[] FrameworksPropertyName = Encoding.UTF8.GetBytes("frameworks");
        private static readonly byte[] FrameworkPropertyName = Encoding.UTF8.GetBytes("framework");
        private static readonly byte[] RestorePropertyName = Encoding.UTF8.GetBytes("restore");
        private static readonly byte[] RuntimesPropertyName = Encoding.UTF8.GetBytes("runtimes");
        private static readonly byte[] SupportsPropertyName = Encoding.UTF8.GetBytes("supports");
        private static readonly byte[] VersionPropertyName = Encoding.UTF8.GetBytes("version");
        private static readonly byte[] AutoReferencedPropertyName = Encoding.UTF8.GetBytes("autoReferenced");
        private static readonly byte[] ExcludePropertyName = Encoding.UTF8.GetBytes("exclude");
        private static readonly byte[] GeneratePathPropertyPropertyName = Encoding.UTF8.GetBytes("generatePathProperty");
        private static readonly byte[] IncludePropertyName = Encoding.UTF8.GetBytes("include");
        private static readonly byte[] NoWarnPropertyName = Encoding.UTF8.GetBytes("noWarn");
        private static readonly byte[] SuppressParentPropertyName = Encoding.UTF8.GetBytes("suppressParent");
        private static readonly byte[] TargetPropertyName = Encoding.UTF8.GetBytes("target");
        private static readonly byte[] VersionOverridePropertyName = Encoding.UTF8.GetBytes("versionOverride");
        private static readonly byte[] VersionCentrallyManagedPropertyName = Encoding.UTF8.GetBytes("versionCentrallyManaged");
        private static readonly byte[] AliasesPropertyName = Encoding.UTF8.GetBytes("aliases");
        private static readonly byte[] NamePropertyName = Encoding.UTF8.GetBytes("name");
        private static readonly byte[] PrivateAssetsPropertyName = Encoding.UTF8.GetBytes("privateAssets");
        private static readonly byte[] CentralPackageVersionsManagementEnabledPropertyName = Encoding.UTF8.GetBytes("centralPackageVersionsManagementEnabled");
        private static readonly byte[] CentralPackageVersionOverrideDisabledPropertyName = Encoding.UTF8.GetBytes("centralPackageVersionOverrideDisabled");
        private static readonly byte[] CentralPackageTransitivePinningEnabledPropertyName = Encoding.UTF8.GetBytes("CentralPackageTransitivePinningEnabled");
        private static readonly byte[] CentralPackageFloatingVersionsEnabledPropertyName = Encoding.UTF8.GetBytes("centralPackageFloatingVersionsEnabled");
        private static readonly byte[] ConfigFilePathsPropertyName = Encoding.UTF8.GetBytes("configFilePaths");
        private static readonly byte[] CrossTargetingPropertyName = Encoding.UTF8.GetBytes("crossTargeting");
        private static readonly byte[] FallbackFoldersPropertyName = Encoding.UTF8.GetBytes("fallbackFolders");
        private static readonly byte[] FilesPropertyName = Encoding.UTF8.GetBytes("files");
        private static readonly byte[] LegacyPackagesDirectoryPropertyName = Encoding.UTF8.GetBytes("legacyPackagesDirectory");
        private static readonly byte[] OriginalTargetFrameworksPropertyName = Encoding.UTF8.GetBytes("originalTargetFrameworks");
        private static readonly byte[] OutputPathPropertyName = Encoding.UTF8.GetBytes("outputPath");
        private static readonly byte[] PackagesConfigPathPropertyName = Encoding.UTF8.GetBytes("packagesConfigPath");
        private static readonly byte[] PackagesPathPropertyName = Encoding.UTF8.GetBytes("packagesPath");
        private static readonly byte[] ProjectJsonPathPropertyName = Encoding.UTF8.GetBytes("projectJsonPath");
        private static readonly byte[] ProjectNamePropertyName = Encoding.UTF8.GetBytes("projectName");
        private static readonly byte[] ProjectPathPropertyName = Encoding.UTF8.GetBytes("projectPath");
        private static readonly byte[] ProjectStylePropertyName = Encoding.UTF8.GetBytes("projectStyle");
        private static readonly byte[] ProjectUniqueNamePropertyName = Encoding.UTF8.GetBytes("projectUniqueName");
        private static readonly byte[] RestoreLockPropertiesPropertyName = Encoding.UTF8.GetBytes("restoreLockProperties");
        private static readonly byte[] NuGetLockFilePathPropertyName = Encoding.UTF8.GetBytes("nuGetLockFilePath");
        private static readonly byte[] RestoreLockedModePropertyName = Encoding.UTF8.GetBytes("restoreLockedMode");
        private static readonly byte[] RestorePackagesWithLockFilePropertyName = Encoding.UTF8.GetBytes("restorePackagesWithLockFile");
        private static readonly byte[] RestoreAuditPropertiesPropertyName = Encoding.UTF8.GetBytes("restoreAuditProperties");
        private static readonly byte[] EnableAuditPropertyName = Encoding.UTF8.GetBytes("enableAudit");
        private static readonly byte[] AuditLevelPropertyName = Encoding.UTF8.GetBytes("auditLevel");
        private static readonly byte[] AuditModePropertyName = Encoding.UTF8.GetBytes("auditMode");
        private static readonly byte[] AuditSuppressionsPropertyName = Encoding.UTF8.GetBytes("suppressedAdvisories");
        private static readonly byte[] SkipContentFileWritePropertyName = Encoding.UTF8.GetBytes("skipContentFileWrite");
        private static readonly byte[] SourcesPropertyName = Encoding.UTF8.GetBytes("sources");
        private static readonly byte[] ValidateRuntimeAssetsPropertyName = Encoding.UTF8.GetBytes("validateRuntimeAssets");
        private static readonly byte[] WarningPropertiesPropertyName = Encoding.UTF8.GetBytes("warningProperties");
        private static readonly byte[] AllWarningsAsErrorsPropertyName = Encoding.UTF8.GetBytes("allWarningsAsErrors");
        private static readonly byte[] WarnAsErrorPropertyName = Encoding.UTF8.GetBytes("warnAsError");
        private static readonly byte[] WarnNotAsErrorPropertyName = Encoding.UTF8.GetBytes("warnNotAsError");
        private static readonly byte[] ExcludeAssetsPropertyName = Encoding.UTF8.GetBytes("excludeAssets");
        private static readonly byte[] IncludeAssetsPropertyName = Encoding.UTF8.GetBytes("includeAssets");
        private static readonly byte[] TargetAliasPropertyName = Encoding.UTF8.GetBytes("targetAlias");
        private static readonly byte[] AssetTargetFallbackPropertyName = Encoding.UTF8.GetBytes("assetTargetFallback");
        private static readonly byte[] SecondaryFrameworkPropertyName = Encoding.UTF8.GetBytes("secondaryFramework");
        private static readonly byte[] CentralPackageVersionsPropertyName = Encoding.UTF8.GetBytes("centralPackageVersions");
        private static readonly byte[] DownloadDependenciesPropertyName = Encoding.UTF8.GetBytes("downloadDependencies");
        private static readonly byte[] FrameworkAssembliesPropertyName = Encoding.UTF8.GetBytes("frameworkAssemblies");
        private static readonly byte[] FrameworkReferencesPropertyName = Encoding.UTF8.GetBytes("frameworkReferences");
        private static readonly byte[] ImportsPropertyName = Encoding.UTF8.GetBytes("imports");
        private static readonly byte[] RuntimeIdentifierGraphPathPropertyName = Encoding.UTF8.GetBytes("runtimeIdentifierGraphPath");
        private static readonly byte[] WarnPropertyName = Encoding.UTF8.GetBytes("warn");
        private static readonly byte[] HashTagImportPropertyName = Encoding.UTF8.GetBytes("#import");
        private static readonly byte[] ProjectReferencesPropertyName = Encoding.UTF8.GetBytes("projectReferences");
        private static readonly byte[] EmptyStringPropertyName = Encoding.UTF8.GetBytes(string.Empty);
        private static readonly byte[] SdkAnalysisLevel = Encoding.UTF8.GetBytes("SdkAnalysisLevel");
        private static readonly byte[] UsingMicrosoftNETSdk = Encoding.UTF8.GetBytes("UsingMicrosoftNETSdk");
        private static readonly byte[] UseLegacyDependencyResolverPropertyName = Encoding.UTF8.GetBytes("restoreUseLegacyDependencyResolver");
        private static readonly byte[] RestoreDoNotWriteDependencyGraphSpecPropertyName = Encoding.UTF8.GetBytes("restoreDoNotWriteDependencyGraphSpec");
        private static readonly byte[] PackagesToPrunePropertyName = Encoding.UTF8.GetBytes("packagesToPrune");

        internal static PackageSpec GetPackageSpecUtf8JsonStreamReader(Stream stream, string name, string packageSpecPath, IEnvironmentVariableReader environmentVariableReader, string snapshotValue = null)
        {
            var reader = new Utf8JsonStreamReader(stream);
            var packageSpec = GetPackageSpec(ref reader, name, packageSpecPath, environmentVariableReader, snapshotValue);

            reader.Dispose();

            return packageSpec;
        }

        internal static PackageSpec GetPackageSpec(ref Utf8JsonStreamReader jsonReader, string name, string packageSpecPath, IEnvironmentVariableReader environmentVariableReader, string snapshotValue = null)
        {
            var packageSpec = new PackageSpec();

            List<CompatibilityProfile> compatibilityProfiles = null;
            List<RuntimeDescription> runtimeDescriptions = null;
            string filePath = name == null ? null : Path.GetFullPath(packageSpecPath);
            if (jsonReader.TokenType == JsonTokenType.StartObject)
            {
                while (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.PropertyName)
                {
                    if (jsonReader.ValueTextEquals(EmptyStringPropertyName))
                    {
                        jsonReader.Skip();
                    }
                    else if (jsonReader.ValueTextEquals(FrameworksPropertyName))
                    {
                        ReadFrameworks(ref jsonReader, packageSpec);
                    }
                    else if (jsonReader.ValueTextEquals(RestorePropertyName))
                    {
                        ReadMSBuildMetadata(ref jsonReader, packageSpec, environmentVariableReader);
                    }
                    else if (jsonReader.ValueTextEquals(RuntimesPropertyName))
                    {
                        runtimeDescriptions = ReadRuntimes(ref jsonReader);
                    }
                    else if (jsonReader.ValueTextEquals(SupportsPropertyName))
                    {
                        compatibilityProfiles = ReadSupports(ref jsonReader);
                    }
                    else if (jsonReader.ValueTextEquals(VersionPropertyName))
                    {
                        string version = jsonReader.ReadNextTokenAsString();
                        if (version != null)
                        {
                            try
                            {
                                packageSpec.Version = PackageSpecUtility.SpecifySnapshot(version, snapshotValue);
                            }
                            catch (Exception ex)
                            {
                                throw FileFormatException.Create(ex, version, packageSpec.FilePath);
                            }
                        }
                    }
                    else
                    {
                        jsonReader.Skip();
                    }
                }
            }
            packageSpec.Name = name;
            packageSpec.FilePath = name == null ? null : Path.GetFullPath(packageSpecPath);

            packageSpec.RuntimeGraph = new RuntimeGraph(
                runtimeDescriptions ?? Enumerable.Empty<RuntimeDescription>(),
                compatibilityProfiles ?? Enumerable.Empty<CompatibilityProfile>());

            packageSpec.Name ??= packageSpec.RestoreMetadata?.ProjectName;

            // Use the project.json path if one is set, otherwise use the project path
            packageSpec.FilePath ??= packageSpec.RestoreMetadata?.ProjectJsonPath
                    ?? packageSpec.RestoreMetadata?.ProjectPath;

            return packageSpec;
        }

        internal static void ReadCentralTransitiveDependencyGroup(
            ref Utf8JsonStreamReader jsonReader,
            out IList<LibraryDependency> results,
            string packageSpecPath)
        {
            results = null;
            if (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.StartObject)
            {
                while (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.PropertyName)
                {
                    var libraryName = jsonReader.GetString();
                    if (string.IsNullOrEmpty(libraryName))
                    {
                        throw FileFormatException.Create(
                            "Unable to resolve dependency ''.",
                            packageSpecPath);
                    }

                    if (jsonReader.Read())
                    {
                        var libraryDependency = ReadLibraryDependency(ref jsonReader, packageSpecPath, libraryName);
                        results ??= [];
                        results.Add(libraryDependency);
                    }
                }
            }
            results ??= Array.Empty<LibraryDependency>();
        }

        private static LibraryDependency ReadLibraryDependency(ref Utf8JsonStreamReader jsonReader, string packageSpecPath, string libraryName)
        {
            var dependencyIncludeFlagsValue = LibraryIncludeFlags.All;
            var dependencyExcludeFlagsValue = LibraryIncludeFlags.None;
            var suppressParentFlagsValue = LibraryIncludeFlagUtils.DefaultSuppressParent;
            string dependencyVersionValue = null;

            if (jsonReader.TokenType == JsonTokenType.String)
            {
                dependencyVersionValue = jsonReader.GetString();
            }
            else if (jsonReader.TokenType == JsonTokenType.StartObject)
            {
                ReadCentralTransitiveDependencyGroupProperties(
                    ref jsonReader,
                    ref dependencyIncludeFlagsValue,
                    ref dependencyExcludeFlagsValue,
                    ref suppressParentFlagsValue,
                    ref dependencyVersionValue);
            }

            VersionRange dependencyVersionRange = null;

            if (!string.IsNullOrEmpty(dependencyVersionValue))
            {
                try
                {
                    dependencyVersionRange = VersionRange.Parse(dependencyVersionValue);
                }
                catch (Exception ex)
                {
                    throw FileFormatException.Create(
                        ex,
                        packageSpecPath);
                }
            }

            if (dependencyVersionRange == null)
            {
                throw FileFormatException.Create(
                    new ArgumentException(Strings.MissingVersionOnDependency),
                    packageSpecPath);
            }

            // the dependency flags are: Include flags - Exclude flags
            var includeFlags = dependencyIncludeFlagsValue & ~dependencyExcludeFlagsValue;
            var libraryDependency = new LibraryDependency()
            {
                LibraryRange = new LibraryRange()
                {
                    Name = libraryName,
                    TypeConstraint = LibraryDependencyTarget.Package,
                    VersionRange = dependencyVersionRange
                },

                IncludeType = includeFlags,
                SuppressParent = suppressParentFlagsValue,
                VersionCentrallyManaged = true,
                ReferenceType = LibraryDependencyReferenceType.Transitive
            };

            return libraryDependency;
        }

        private static void ReadCentralTransitiveDependencyGroupProperties(
            ref Utf8JsonStreamReader jsonReader,
            ref LibraryIncludeFlags dependencyIncludeFlagsValue,
            ref LibraryIncludeFlags dependencyExcludeFlagsValue,
            ref LibraryIncludeFlags suppressParentFlagsValue,
            ref string dependencyVersionValue)
        {
            while (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.PropertyName)
            {
                if (jsonReader.ValueTextEquals(ExcludePropertyName))
                {
                    var values = jsonReader.ReadDelimitedString();
                    dependencyExcludeFlagsValue = LibraryIncludeFlagUtils.GetFlags(values);
                }
                else if (jsonReader.ValueTextEquals(IncludePropertyName))
                {
                    var values = jsonReader.ReadDelimitedString();
                    dependencyIncludeFlagsValue = LibraryIncludeFlagUtils.GetFlags(values);
                }
                else if (jsonReader.ValueTextEquals(SuppressParentPropertyName))
                {
                    var values = jsonReader.ReadDelimitedString();
                    suppressParentFlagsValue = LibraryIncludeFlagUtils.GetFlags(values);
                }
                else if (jsonReader.ValueTextEquals(VersionPropertyName))
                {
                    if (jsonReader.Read())
                    {
                        dependencyVersionValue = jsonReader.GetString();
                    }
                }
                else
                {
                    jsonReader.Skip();
                }
            }
        }

        private static void ReadDependencies(
            ref Utf8JsonStreamReader jsonReader,
            IList<LibraryDependency> results,
            string packageSpecPath,
            bool isGacOrFrameworkReference)
        {
            if (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.StartObject)
            {
                while (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.PropertyName)
                {
                    var propertyName = jsonReader.GetString();
                    if (string.IsNullOrEmpty(propertyName))
                    {
                        throw FileFormatException.Create("Unable to resolve dependency ''.", packageSpecPath);
                    }

                    // Support
                    // "dependencies" : {
                    //    "Name" : "1.0"
                    // }

                    if (jsonReader.Read())
                    {
                        var dependencyIncludeFlagsValue = LibraryIncludeFlags.All;
                        var dependencyExcludeFlagsValue = LibraryIncludeFlags.None;
                        var suppressParentFlagsValue = LibraryIncludeFlagUtils.DefaultSuppressParent;
                        ImmutableArray<NuGetLogCode> noWarn = [];

                        // This method handles both the dependencies and framework assembly sections.
                        // Framework references should be limited to references.
                        // Dependencies should allow everything but framework references.
                        LibraryDependencyTarget targetFlagsValue = isGacOrFrameworkReference
                            ? LibraryDependencyTarget.Reference
                            : LibraryDependencyTarget.All & ~LibraryDependencyTarget.Reference;

                        var autoReferenced = false;
                        var generatePathProperty = false;
                        var versionCentrallyManaged = false;
                        string aliases = null;
                        string dependencyVersionValue = null;
                        VersionRange versionOverride = null;

                        if (jsonReader.TokenType == JsonTokenType.String)
                        {
                            dependencyVersionValue = jsonReader.GetString();
                        }
                        else if (jsonReader.TokenType == JsonTokenType.StartObject)
                        {
                            while (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.PropertyName)
                            {
                                if (jsonReader.ValueTextEquals(AutoReferencedPropertyName))
                                {
                                    autoReferenced = jsonReader.ReadNextTokenAsBoolOrFalse();
                                }
                                else if (jsonReader.ValueTextEquals(ExcludePropertyName))
                                {
                                    var values = jsonReader.ReadDelimitedString();
                                    dependencyExcludeFlagsValue = LibraryIncludeFlagUtils.GetFlags(values);
                                }
                                else if (jsonReader.ValueTextEquals(GeneratePathPropertyPropertyName))
                                {
                                    generatePathProperty = jsonReader.ReadNextTokenAsBoolOrFalse();
                                }
                                else if (jsonReader.ValueTextEquals(IncludePropertyName))
                                {
                                    var values = jsonReader.ReadDelimitedString();
                                    dependencyIncludeFlagsValue = LibraryIncludeFlagUtils.GetFlags(values);
                                }
                                else if (jsonReader.ValueTextEquals(NoWarnPropertyName))
                                {
                                    noWarn = ReadNuGetLogCodesList(ref jsonReader);
                                }
                                else if (jsonReader.ValueTextEquals(SuppressParentPropertyName))
                                {
                                    var values = jsonReader.ReadDelimitedString();
                                    suppressParentFlagsValue = LibraryIncludeFlagUtils.GetFlags(values);
                                }
                                else if (jsonReader.ValueTextEquals(TargetPropertyName))
                                {
                                    targetFlagsValue = ReadTarget(ref jsonReader, packageSpecPath, targetFlagsValue);
                                }
                                else if (jsonReader.ValueTextEquals(VersionPropertyName))
                                {
                                    if (jsonReader.Read())
                                    {
                                        dependencyVersionValue = jsonReader.GetString();
                                    }
                                }
                                else if (jsonReader.ValueTextEquals(VersionOverridePropertyName))
                                {
                                    if (jsonReader.Read())
                                    {
                                        var versionPropValue = jsonReader.GetString();
                                        try
                                        {
                                            versionOverride = VersionRange.Parse(versionPropValue);
                                        }
                                        catch (Exception ex)
                                        {
                                            throw FileFormatException.Create(ex, packageSpecPath);
                                        }
                                    }
                                }
                                else if (jsonReader.ValueTextEquals(VersionCentrallyManagedPropertyName))
                                {
                                    versionCentrallyManaged = jsonReader.ReadNextTokenAsBoolOrFalse();
                                }
                                else if (jsonReader.ValueTextEquals(AliasesPropertyName))
                                {
                                    aliases = jsonReader.ReadNextTokenAsString();
                                }
                                else
                                {
                                    jsonReader.Skip();
                                }
                            }
                        }

                        VersionRange dependencyVersionRange = null;

                        if (!string.IsNullOrEmpty(dependencyVersionValue))
                        {
                            try
                            {
                                dependencyVersionRange = VersionRange.Parse(dependencyVersionValue);
                            }
                            catch (Exception ex)
                            {
                                throw FileFormatException.Create(
                                    ex,
                                    packageSpecPath);
                            }
                        }

                        // Projects and References may have empty version ranges, Packages may not
                        if (dependencyVersionRange == null)
                        {
                            if ((targetFlagsValue & LibraryDependencyTarget.Package) == LibraryDependencyTarget.Package)
                            {
                                throw FileFormatException.Create(
                                new ArgumentException(Strings.MissingVersionOnDependency),
                                packageSpecPath);
                            }
                            else
                            {
                                // Projects and references with no version property allow all versions
                                dependencyVersionRange = VersionRange.All;
                            }
                        }

                        // the dependency flags are: Include flags - Exclude flags
                        var includeFlags = dependencyIncludeFlagsValue & ~dependencyExcludeFlagsValue;
                        var libraryDependency = new LibraryDependency()
                        {
                            LibraryRange = new LibraryRange()
                            {
                                Name = propertyName,
                                TypeConstraint = targetFlagsValue,
                                VersionRange = dependencyVersionRange
                            },
                            IncludeType = includeFlags,
                            SuppressParent = suppressParentFlagsValue,
                            AutoReferenced = autoReferenced,
                            GeneratePathProperty = generatePathProperty,
                            VersionCentrallyManaged = versionCentrallyManaged,
                            Aliases = aliases,
                            // The ReferenceType is not persisted to the assets file
                            // Default to LibraryDependencyReferenceType.Direct on Read
                            ReferenceType = LibraryDependencyReferenceType.Direct,
                            VersionOverride = versionOverride,
                            NoWarn = noWarn,
                        };

                        results.Add(libraryDependency);
                    }
                }
            }
        }

        private static void ReadCentralPackageVersions(
            ref Utf8JsonStreamReader jsonReader,
            IDictionary<string, CentralPackageVersion> centralPackageVersions,
            string filePath)
        {
            if (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.StartObject)
            {
                while (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.PropertyName)
                {
                    var propertyName = jsonReader.GetString();

                    if (string.IsNullOrEmpty(propertyName))
                    {
                        throw FileFormatException.Create("Unable to resolve central version ''.", filePath);
                    }

                    string version = jsonReader.ReadNextTokenAsString();

                    if (string.IsNullOrEmpty(version))
                    {
                        throw FileFormatException.Create("The version cannot be null or empty.", filePath);
                    }

                    centralPackageVersions[propertyName] = new CentralPackageVersion(propertyName, VersionRange.Parse(version));
                }
            }
        }

        private static void ReadPackagesToPrune(
            ref Utf8JsonStreamReader jsonReader,
            IDictionary<string, PrunePackageReference> packagesToPrune,
            string filePath)
        {
            if (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.StartObject)
            {
                while (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.PropertyName)
                {
                    var propertyName = jsonReader.GetString();

                    if (string.IsNullOrEmpty(propertyName))
                    {
                        throw FileFormatException.Create("Unable to resolve package to prune ''.", filePath);
                    }

                    string version = jsonReader.ReadNextTokenAsString();

                    if (string.IsNullOrEmpty(version))
                    {
                        throw FileFormatException.Create("The version cannot be null or empty.", filePath);
                    }

                    packagesToPrune[propertyName] = new PrunePackageReference(propertyName, VersionRange.Parse(version));
                }
            }
        }

        private static CompatibilityProfile ReadCompatibilityProfile(ref Utf8JsonStreamReader jsonReader, string profileName)
        {
            List<FrameworkRuntimePair> sets = null;

            if (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.StartObject)
            {
                while (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.PropertyName)
                {
                    var propertyName = jsonReader.GetString();
                    sets ??= [];

                    IReadOnlyList<string> values = jsonReader.ReadNextStringOrArrayOfStringsAsReadOnlyList() ?? Array.Empty<string>();

                    IEnumerable<FrameworkRuntimePair> profiles = ReadCompatibilitySets(values, propertyName);

                    sets.AddRange(profiles);
                }
            }
            return new CompatibilityProfile(profileName, sets ?? Enumerable.Empty<FrameworkRuntimePair>());
        }

        private static IEnumerable<FrameworkRuntimePair> ReadCompatibilitySets(IReadOnlyList<string> values, string compatibilitySetName)
        {
            NuGetFramework framework = NuGetFramework.Parse(compatibilitySetName);

            foreach (string value in values)
            {
                yield return new FrameworkRuntimePair(framework, value);
            }
        }

        private static void ReadDownloadDependencies(
            ref Utf8JsonStreamReader jsonReader,
            IList<DownloadDependency> downloadDependencies,
            string packageSpecPath)
        {
            var seenIds = new HashSet<string>();

            if (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.StartArray)
            {
                do
                {
                    string name = null;
                    string versionValue = null;
                    var isNameDefined = false;

                    if (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.StartObject)
                    {
                        while (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.PropertyName)
                        {
                            if (jsonReader.ValueTextEquals(NamePropertyName))
                            {
                                isNameDefined = true;
                                name = jsonReader.ReadNextTokenAsString();
                            }
                            else if (jsonReader.ValueTextEquals(VersionPropertyName))
                            {
                                versionValue = jsonReader.ReadNextTokenAsString();
                            }
                            else
                            {
                                jsonReader.Skip();
                            }
                        }
                    }

                    if (jsonReader.TokenType == JsonTokenType.EndArray)
                    {
                        break;
                    }

                    if (!isNameDefined)
                    {
                        throw FileFormatException.Create(
                            "Unable to resolve downloadDependency ''.",
                            packageSpecPath);
                    }

                    if (!seenIds.Add(name))
                    {
                        // package ID already seen, only use first definition.
                        continue;
                    }

                    if (string.IsNullOrEmpty(versionValue))
                    {
                        throw FileFormatException.Create(
                            "The version cannot be null or empty",
                            packageSpecPath);
                    }

                    var versions = new LazyStringSplit(versionValue, VersionSeparator);

                    foreach (string singleVersionValue in versions)
                    {
                        if (string.IsNullOrEmpty(singleVersionValue))
                        {
                            continue;
                        }

                        try
                        {
                            VersionRange version = VersionRange.Parse(singleVersionValue);

                            downloadDependencies.Add(new DownloadDependency(name, version));
                        }
                        catch (Exception ex)
                        {
                            throw FileFormatException.Create(
                               ex,
                               packageSpecPath);
                        }
                    }
                } while (jsonReader.TokenType == JsonTokenType.EndObject);
            }
        }

        private static void ReadFrameworkReferences(
            ref Utf8JsonStreamReader jsonReader,
            ISet<FrameworkDependency> frameworkReferences,
            string packageSpecPath)
        {
            if (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.StartObject)
            {
                while (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.PropertyName)
                {
                    var frameworkName = jsonReader.GetString();
                    if (string.IsNullOrEmpty(frameworkName))
                    {
                        throw FileFormatException.Create(
                            "Unable to resolve frameworkReference.",
                            packageSpecPath);
                    }

                    var privateAssets = FrameworkDependencyFlagsUtils.Default;

                    if (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.StartObject)
                    {
                        while (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.PropertyName)
                        {
                            if (jsonReader.ValueTextEquals(PrivateAssetsPropertyName))
                            {
                                IEnumerable<string> strings = jsonReader.ReadDelimitedString();

                                privateAssets = FrameworkDependencyFlagsUtils.GetFlags(strings);
                            }
                            else
                            {
                                jsonReader.Skip();
                            }
                        }
                    }

                    frameworkReferences.Add(new FrameworkDependency(frameworkName, privateAssets));
                }
            }
        }

        private static void ReadFrameworks(ref Utf8JsonStreamReader reader, PackageSpec packageSpec)
        {
            if (reader.Read() && reader.TokenType == JsonTokenType.StartObject)
            {
                while (reader.Read() && reader.TokenType == JsonTokenType.PropertyName)
                {
                    try
                    {
                        ReadTargetFrameworks(packageSpec, ref reader);
                    }
                    catch (Exception ex)
                    {
                        throw FileFormatException.Create(ex, packageSpec.FilePath);
                    }
                }
            }
        }

        private static void ReadImports(PackageSpec packageSpec, ref Utf8JsonStreamReader jsonReader, List<NuGetFramework> importFrameworks)
        {
            IReadOnlyList<string> imports = jsonReader.ReadNextStringOrArrayOfStringsAsReadOnlyList();

            if (imports != null && imports.Count > 0)
            {
                foreach (string import in imports.Where(element => !string.IsNullOrEmpty(element)))
                {
                    NuGetFramework framework = NuGetFramework.Parse(import);

                    if (!framework.IsSpecificFramework)
                    {
                        throw FileFormatException.Create(
                            string.Format(
                                CultureInfo.CurrentCulture,
                                Strings.Log_InvalidImportFramework,
                                import,
                                packageSpec.FilePath),
                            packageSpec.FilePath);
                    }

                    importFrameworks.Add(framework);
                }
            }
        }

        private static void ReadMSBuildMetadata(ref Utf8JsonStreamReader jsonReader, PackageSpec packageSpec, IEnvironmentVariableReader environmentVariableReader)
        {
            var centralPackageVersionsManagementEnabled = false;
            var centralPackageVersionOverrideDisabled = false;
            var CentralPackageTransitivePinningEnabled = false;
            var centralPackageFloatingVersionsEnabled = false;
            List<string> configFilePaths = null;
            var crossTargeting = false;
            List<string> fallbackFolders = null;
            List<ProjectRestoreMetadataFile> files = null;
            var legacyPackagesDirectory = false;
            List<string> originalTargetFrameworks = null;
            string outputPath = null;
            string packagesConfigPath = null;
            string packagesPath = null;
            string projectJsonPath = null;
            string projectName = null;
            string projectPath = null;
            ProjectStyle? projectStyle = null;
            string projectUniqueName = null;
            RestoreLockProperties restoreLockProperties = null;
            var skipContentFileWrite = false;
            List<PackageSource> sources = null;
            IList<ProjectRestoreMetadataFrameworkInfo> targetFrameworks = null;
            var validateRuntimeAssets = false;
            WarningProperties warningProperties = null;
            RestoreAuditProperties auditProperties = null;
            bool useMacros = MSBuildStringUtility.IsTrue(environmentVariableReader.GetEnvironmentVariable(MacroStringsUtility.NUGET_ENABLE_EXPERIMENTAL_MACROS));
            var userSettingsDirectory = NuGetEnvironment.GetFolderPath(NuGetFolderPath.UserSettingsDirectory);
            bool usingMicrosoftNetSdk = true;
            NuGetVersion sdkAnalysisLevel = null;
            bool useLegacyDependencyResolver = false;
            bool restoreDoNotWriteDependencyGraphSpec = false;

            if (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.StartObject)
            {
                while (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.PropertyName)
                {
                    if (jsonReader.ValueTextEquals(CentralPackageVersionsManagementEnabledPropertyName))
                    {
                        centralPackageVersionsManagementEnabled = jsonReader.ReadNextTokenAsBoolOrFalse();
                    }
                    else if (jsonReader.ValueTextEquals(CentralPackageVersionOverrideDisabledPropertyName))
                    {
                        centralPackageVersionOverrideDisabled = jsonReader.ReadNextTokenAsBoolOrFalse();
                    }
                    else if (jsonReader.ValueTextEquals(CentralPackageTransitivePinningEnabledPropertyName))
                    {
                        CentralPackageTransitivePinningEnabled = jsonReader.ReadNextTokenAsBoolOrFalse();
                    }
                    else if (jsonReader.ValueTextEquals(CentralPackageFloatingVersionsEnabledPropertyName))
                    {
                        centralPackageFloatingVersionsEnabled = jsonReader.ReadNextTokenAsBoolOrFalse();
                    }
                    else if (jsonReader.ValueTextEquals(ConfigFilePathsPropertyName))
                    {
                        jsonReader.Read();
                        configFilePaths = jsonReader.ReadStringArrayAsIList() as List<string>;
                        ExtractMacros(configFilePaths, userSettingsDirectory, useMacros);
                    }
                    else if (jsonReader.ValueTextEquals(CrossTargetingPropertyName))
                    {
                        crossTargeting = jsonReader.ReadNextTokenAsBoolOrFalse();
                    }
                    else if (jsonReader.ValueTextEquals(FallbackFoldersPropertyName))
                    {
                        jsonReader.Read();
                        fallbackFolders = jsonReader.ReadStringArrayAsIList() as List<string>;
                        ExtractMacros(fallbackFolders, userSettingsDirectory, useMacros);
                    }
                    else if (jsonReader.ValueTextEquals(FilesPropertyName))
                    {
                        if (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.StartObject)
                        {
                            while (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.PropertyName)
                            {
                                var filePropertyName = jsonReader.GetString();
                                files ??= [];

                                files.Add(new ProjectRestoreMetadataFile(filePropertyName, jsonReader.ReadNextTokenAsString()));
                            }
                        }
                    }
                    else if (jsonReader.ValueTextEquals(FrameworksPropertyName))
                    {
                        targetFrameworks = ReadTargetFrameworks(ref jsonReader);
                    }
                    else if (jsonReader.ValueTextEquals(LegacyPackagesDirectoryPropertyName))
                    {
                        legacyPackagesDirectory = jsonReader.ReadNextTokenAsBoolOrFalse();
                    }
                    else if (jsonReader.ValueTextEquals(OriginalTargetFrameworksPropertyName))
                    {
                        jsonReader.Read();
                        originalTargetFrameworks = jsonReader.ReadStringArrayAsIList() as List<string>;
                    }
                    else if (jsonReader.ValueTextEquals(OutputPathPropertyName))
                    {
                        outputPath = jsonReader.ReadNextTokenAsString();
                    }
                    else if (jsonReader.ValueTextEquals(PackagesConfigPathPropertyName))
                    {
                        packagesConfigPath = jsonReader.ReadNextTokenAsString();
                    }
                    else if (jsonReader.ValueTextEquals(PackagesPathPropertyName))
                    {
                        packagesPath = ExtractMacro(jsonReader.ReadNextTokenAsString(), userSettingsDirectory, useMacros);
                    }
                    else if (jsonReader.ValueTextEquals(ProjectJsonPathPropertyName))
                    {
                        projectJsonPath = jsonReader.ReadNextTokenAsString();
                    }
                    else if (jsonReader.ValueTextEquals(ProjectNamePropertyName))
                    {
                        projectName = jsonReader.ReadNextTokenAsString();
                    }
                    else if (jsonReader.ValueTextEquals(ProjectPathPropertyName))
                    {
                        projectPath = jsonReader.ReadNextTokenAsString();
                    }
                    else if (jsonReader.ValueTextEquals(ProjectStylePropertyName))
                    {
                        string projectStyleString = jsonReader.ReadNextTokenAsString();

                        if (!string.IsNullOrEmpty(projectStyleString)
                            && Enum.TryParse(projectStyleString, ignoreCase: true, result: out ProjectStyle projectStyleValue))
                        {
                            projectStyle = projectStyleValue;
                        }
                    }
                    else if (jsonReader.ValueTextEquals(ProjectUniqueNamePropertyName))
                    {
                        projectUniqueName = ExtractMacro(jsonReader.ReadNextTokenAsString(), userSettingsDirectory, useMacros);
                    }
                    else if (jsonReader.ValueTextEquals(RestoreLockPropertiesPropertyName))
                    {
                        string nuGetLockFilePath = null;
                        var restoreLockedMode = false;
                        string restorePackagesWithLockFile = null;

                        if (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.StartObject)
                        {
                            while (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.PropertyName)
                            {
                                if (jsonReader.ValueTextEquals(NuGetLockFilePathPropertyName))
                                {
                                    nuGetLockFilePath = jsonReader.ReadNextTokenAsString();
                                }
                                else if (jsonReader.ValueTextEquals(RestoreLockedModePropertyName))
                                {
                                    restoreLockedMode = jsonReader.ReadNextTokenAsBoolOrFalse();
                                }
                                else if (jsonReader.ValueTextEquals(RestorePackagesWithLockFilePropertyName))
                                {
                                    restorePackagesWithLockFile = jsonReader.ReadNextTokenAsString();
                                }
                                else
                                {
                                    jsonReader.Skip();
                                }
                            }
                        }
                        restoreLockProperties = new RestoreLockProperties(restorePackagesWithLockFile, nuGetLockFilePath, restoreLockedMode);
                    }
                    else if (jsonReader.ValueTextEquals(RestoreAuditPropertiesPropertyName))
                    {
                        string enableAudit = null, auditLevel = null, auditMode = null;
                        HashSet<string> suppressedAdvisories = null;

                        if (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.StartObject)
                        {
                            while (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.PropertyName)
                            {
                                if (jsonReader.ValueTextEquals(EnableAuditPropertyName))
                                {
                                    enableAudit = jsonReader.ReadNextTokenAsString();
                                }
                                else if (jsonReader.ValueTextEquals(AuditLevelPropertyName))
                                {
                                    auditLevel = jsonReader.ReadNextTokenAsString();
                                }
                                else if (jsonReader.ValueTextEquals(AuditModePropertyName))
                                {
                                    auditMode = jsonReader.ReadNextTokenAsString();
                                }
                                else if (jsonReader.ValueTextEquals(AuditSuppressionsPropertyName))
                                {
                                    suppressedAdvisories = ReadSuppressedAdvisories(ref jsonReader);
                                }
                                else
                                {
                                    jsonReader.Skip();
                                }
                            }
                        }
                        auditProperties = new RestoreAuditProperties()
                        {
                            EnableAudit = enableAudit,
                            AuditLevel = auditLevel,
                            AuditMode = auditMode,
                            SuppressedAdvisories = suppressedAdvisories,
                        };
                    }
                    else if (jsonReader.ValueTextEquals(SkipContentFileWritePropertyName))
                    {
                        skipContentFileWrite = jsonReader.ReadNextTokenAsBoolOrFalse();
                    }
                    else if (jsonReader.ValueTextEquals(SourcesPropertyName))
                    {
                        if (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.StartObject)
                        {
                            while (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.PropertyName)
                            {
                                var sourcePropertyName = jsonReader.GetString();
                                sources ??= [];

                                sources.Add(new PackageSource(sourcePropertyName));
                                jsonReader.Skip();
                            }
                        }
                    }
                    else if (jsonReader.ValueTextEquals(ValidateRuntimeAssetsPropertyName))
                    {
                        validateRuntimeAssets = jsonReader.ReadNextTokenAsBoolOrFalse();
                    }
                    else if (jsonReader.ValueTextEquals(WarningPropertiesPropertyName))
                    {
                        var allWarningsAsErrors = false;
                        var noWarn = new HashSet<NuGetLogCode>();
                        var warnAsError = new HashSet<NuGetLogCode>();
                        var warningsNotAsErrors = new HashSet<NuGetLogCode>();

                        if (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.StartObject)
                        {
                            while (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.PropertyName)
                            {
                                if (jsonReader.ValueTextEquals(AllWarningsAsErrorsPropertyName))
                                {
                                    allWarningsAsErrors = jsonReader.ReadNextTokenAsBoolOrFalse();
                                }
                                else if (jsonReader.ValueTextEquals(NoWarnPropertyName))
                                {
                                    ReadNuGetLogCodes(ref jsonReader, noWarn);
                                }
                                else if (jsonReader.ValueTextEquals(WarnAsErrorPropertyName))
                                {
                                    ReadNuGetLogCodes(ref jsonReader, warnAsError);
                                }
                                else if (jsonReader.ValueTextEquals(WarnNotAsErrorPropertyName))
                                {
                                    ReadNuGetLogCodes(ref jsonReader, warningsNotAsErrors);
                                }
                                else
                                {
                                    jsonReader.Skip();
                                }
                            }
                        }

                        warningProperties = new WarningProperties(warnAsError, noWarn, allWarningsAsErrors, warningsNotAsErrors);
                    }
                    else if (jsonReader.ValueTextEquals(UsingMicrosoftNETSdk))
                    {
                        usingMicrosoftNetSdk = jsonReader.ReadNextTokenAsBoolOrThrowAnException(UsingMicrosoftNETSdk, Strings.Invalid_AttributeValue);
                    }
                    else if (jsonReader.ValueTextEquals(SdkAnalysisLevel))
                    {
                        string sdkAnalysisLevelString = jsonReader.ReadNextTokenAsString();

                        if (!string.IsNullOrEmpty(sdkAnalysisLevelString))
                        {
                            try
                            {
                                sdkAnalysisLevel = new NuGetVersion(sdkAnalysisLevelString);
                            }
                            catch (ArgumentException ex)
                            {
                                throw new ArgumentException(
                                    string.Format(CultureInfo.CurrentCulture,
                                    Strings.Invalid_AttributeValue,
                                    Encoding.UTF8.GetString(SdkAnalysisLevel),
                                    sdkAnalysisLevelString,
                                    "9.0.100"),
                                    ex);
                            }
                        }
                    }
                    else if (jsonReader.ValueTextEquals(UseLegacyDependencyResolverPropertyName))
                    {
                        useLegacyDependencyResolver = jsonReader.ReadNextTokenAsBoolOrThrowAnException(UseLegacyDependencyResolverPropertyName, Strings.Invalid_AttributeValue);
                    }
                    else if (jsonReader.ValueTextEquals(RestoreDoNotWriteDependencyGraphSpecPropertyName))
                    {
                        restoreDoNotWriteDependencyGraphSpec = jsonReader.ReadNextTokenAsBoolOrThrowAnException(RestoreDoNotWriteDependencyGraphSpecPropertyName, Strings.Invalid_AttributeValue);
                    }
                    else
                    {
                        jsonReader.Skip();
                    }
                }
            }

            ProjectRestoreMetadata msbuildMetadata;
            if (projectStyle == ProjectStyle.PackagesConfig)
            {
                msbuildMetadata = new PackagesConfigProjectRestoreMetadata()
                {
                    PackagesConfigPath = packagesConfigPath
                };
            }
            else
            {
                msbuildMetadata = new ProjectRestoreMetadata();
            }

            msbuildMetadata.CentralPackageVersionsEnabled = centralPackageVersionsManagementEnabled;
            msbuildMetadata.CentralPackageVersionOverrideDisabled = centralPackageVersionOverrideDisabled;
            msbuildMetadata.CentralPackageTransitivePinningEnabled = CentralPackageTransitivePinningEnabled;
            msbuildMetadata.CentralPackageFloatingVersionsEnabled = centralPackageFloatingVersionsEnabled;
            msbuildMetadata.RestoreAuditProperties = auditProperties;
            msbuildMetadata.SdkAnalysisLevel = sdkAnalysisLevel;
            msbuildMetadata.UsingMicrosoftNETSdk = usingMicrosoftNetSdk;
            msbuildMetadata.UseLegacyDependencyResolver = useLegacyDependencyResolver;
            msbuildMetadata.RestoreDoNotWriteDependencyGraphSpec = restoreDoNotWriteDependencyGraphSpec;

            if (configFilePaths != null)
            {
                msbuildMetadata.ConfigFilePaths = configFilePaths;
            }

            msbuildMetadata.CrossTargeting = crossTargeting;

            if (fallbackFolders != null)
            {
                msbuildMetadata.FallbackFolders = fallbackFolders;
            }

            if (files != null)
            {
                msbuildMetadata.Files = files;
            }

            msbuildMetadata.LegacyPackagesDirectory = legacyPackagesDirectory;

            if (originalTargetFrameworks != null)
            {
                msbuildMetadata.OriginalTargetFrameworks = originalTargetFrameworks;
            }

            msbuildMetadata.OutputPath = outputPath;
            msbuildMetadata.PackagesPath = packagesPath;
            msbuildMetadata.ProjectJsonPath = projectJsonPath;
            msbuildMetadata.ProjectName = projectName;
            msbuildMetadata.ProjectPath = projectPath;

            if (projectStyle.HasValue)
            {
                msbuildMetadata.ProjectStyle = projectStyle.Value;
            }

            msbuildMetadata.ProjectUniqueName = projectUniqueName;

            if (restoreLockProperties != null)
            {
                msbuildMetadata.RestoreLockProperties = restoreLockProperties;
            }

            msbuildMetadata.SkipContentFileWrite = skipContentFileWrite;

            if (sources != null)
            {
                msbuildMetadata.Sources = sources;
            }

            if (targetFrameworks != null)
            {
                msbuildMetadata.TargetFrameworks = targetFrameworks;
            }

            msbuildMetadata.ValidateRuntimeAssets = validateRuntimeAssets;

            if (warningProperties != null)
            {
                msbuildMetadata.ProjectWideWarningProperties = warningProperties;
            }

            packageSpec.RestoreMetadata = msbuildMetadata;
        }

        private static void ReadNuGetLogCodes(ref Utf8JsonStreamReader jsonReader, HashSet<NuGetLogCode> hashCodes)
        {
            if (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.StartArray)
            {
                while (jsonReader.Read() && jsonReader.TokenType != JsonTokenType.EndArray)
                {
                    if (jsonReader.TokenType == JsonTokenType.String && Enum.TryParse(jsonReader.GetString(), out NuGetLogCode code))
                    {
                        hashCodes.Add(code);
                    }
                }
            }
        }

        private static ImmutableArray<NuGetLogCode> ReadNuGetLogCodesList(ref Utf8JsonStreamReader jsonReader)
        {
            NuGetLogCode[] items = null;
            var index = 0;

            if (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.StartArray)
            {
                while (jsonReader.Read() && jsonReader.TokenType != JsonTokenType.EndArray)
                {
                    if (jsonReader.TokenType == JsonTokenType.String && Enum.TryParse(jsonReader.GetString(), out NuGetLogCode code))
                    {
                        if (items == null)
                        {
                            items = ArrayPool<NuGetLogCode>.Shared.Rent(16);
                        }
                        else if (items.Length == index)
                        {
                            var oldItems = items;

                            items = ArrayPool<NuGetLogCode>.Shared.Rent(items.Length * 2);
                            oldItems.CopyTo(items, index: 0);

                            ArrayPool<NuGetLogCode>.Shared.Return(oldItems);
                        }

                        items[index++] = code;
                    }
                }
            }

            if (items == null)
            {
                return [];
            }

            var retVal = items.AsSpan(0, index).ToImmutableArray();
            ArrayPool<NuGetLogCode>.Shared.Return(items);

            return retVal;
        }

        private static RuntimeDependencySet ReadRuntimeDependencySet(ref Utf8JsonStreamReader jsonReader, string dependencySetName)
        {
            List<RuntimePackageDependency> dependencies = null;

            if (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.StartObject)
            {
                while (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.PropertyName)
                {
                    var propertyName = jsonReader.GetString();
                    dependencies ??= [];

                    var dependency = new RuntimePackageDependency(propertyName, VersionRange.Parse(jsonReader.ReadNextTokenAsString()));

                    dependencies.Add(dependency);
                }
            }

            return new RuntimeDependencySet(
                dependencySetName,
                dependencies);
        }

        private static RuntimeDescription ReadRuntimeDescription(ref Utf8JsonStreamReader jsonReader, string runtimeName)
        {
            List<string> inheritedRuntimes = null;
            List<RuntimeDependencySet> additionalDependencies = null;

            if (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.StartObject)
            {
                while (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.PropertyName)
                {
                    if (jsonReader.ValueTextEquals(HashTagImportPropertyName))
                    {
                        jsonReader.Read();
                        inheritedRuntimes = jsonReader.ReadStringArrayAsIList() as List<string>;
                    }
                    else
                    {
                        var propertyName = jsonReader.GetString();
                        additionalDependencies ??= [];

                        RuntimeDependencySet dependency = ReadRuntimeDependencySet(ref jsonReader, propertyName);

                        additionalDependencies.Add(dependency);
                    }
                }
            }

            return new RuntimeDescription(
                runtimeName,
                inheritedRuntimes,
                additionalDependencies);
        }

        private static List<RuntimeDescription> ReadRuntimes(ref Utf8JsonStreamReader jsonReader)
        {
            List<RuntimeDescription> runtimeDescriptions = null;

            if (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.StartObject)
            {
                while (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.PropertyName)
                {
                    RuntimeDescription runtimeDescription = ReadRuntimeDescription(ref jsonReader, jsonReader.GetString());
                    runtimeDescriptions ??= [];
                    runtimeDescriptions.Add(runtimeDescription);
                }
            }

            return runtimeDescriptions;
        }

        private static List<CompatibilityProfile> ReadSupports(ref Utf8JsonStreamReader jsonReader)
        {
            List<CompatibilityProfile> compatibilityProfiles = null;

            if (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.StartObject)
            {
                while (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.PropertyName)
                {
                    var propertyName = jsonReader.GetString();
                    CompatibilityProfile compatibilityProfile = ReadCompatibilityProfile(ref jsonReader, propertyName);
                    compatibilityProfiles ??= [];
                    compatibilityProfiles.Add(compatibilityProfile);
                }
            }
            return compatibilityProfiles;
        }

        private static LibraryDependencyTarget ReadTarget(
           ref Utf8JsonStreamReader jsonReader,
           string packageSpecPath,
           LibraryDependencyTarget targetFlagsValue)
        {
            if (jsonReader.Read())
            {
                var targetString = jsonReader.GetString();

                targetFlagsValue = LibraryDependencyTargetUtils.Parse(targetString);

                // Verify that the value specified is package, project, or external project
                if (!ValidateDependencyTarget(targetFlagsValue))
                {
                    string message = string.Format(
                        CultureInfo.CurrentCulture,
                        Strings.InvalidDependencyTarget,
                        targetString);
                    throw FileFormatException.Create(
                      message,
                      packageSpecPath);
                }
            }

            return targetFlagsValue;
        }

        private static List<ProjectRestoreMetadataFrameworkInfo> ReadTargetFrameworks(ref Utf8JsonStreamReader jsonReader)
        {
            List<ProjectRestoreMetadataFrameworkInfo> targetFrameworks = null;

            if (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.StartObject)
            {
                while (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.PropertyName)
                {
                    var key = jsonReader.GetString();
                    string frameworkProperty = null;
                    string targetAlias = string.Empty;
                    List<ProjectRestoreReference> projectReferences = new();

                    if (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.StartObject)
                    {
                        while (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.PropertyName)
                        {
                            if (jsonReader.ValueTextEquals(ProjectReferencesPropertyName))
                            {
                                if (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.StartObject)
                                {
                                    while (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.PropertyName)
                                    {
                                        var projectReferencePropertyName = jsonReader.GetString();
                                        string excludeAssets = null;
                                        string includeAssets = null;
                                        string privateAssets = null;
                                        string projectReferenceProjectPath = null;

                                        if (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.StartObject)
                                        {
                                            while (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.PropertyName)
                                            {
                                                if (jsonReader.ValueTextEquals(ExcludeAssetsPropertyName))
                                                {
                                                    excludeAssets = jsonReader.ReadNextTokenAsString();
                                                }
                                                else if (jsonReader.ValueTextEquals(IncludeAssetsPropertyName))
                                                {
                                                    includeAssets = jsonReader.ReadNextTokenAsString();
                                                }
                                                else if (jsonReader.ValueTextEquals(PrivateAssetsPropertyName))
                                                {
                                                    privateAssets = jsonReader.ReadNextTokenAsString();
                                                }
                                                else if (jsonReader.ValueTextEquals(ProjectPathPropertyName))
                                                {
                                                    projectReferenceProjectPath = jsonReader.ReadNextTokenAsString();
                                                }
                                                else
                                                {
                                                    jsonReader.Skip();
                                                }

                                            }
                                        }

                                        projectReferences.Add(new ProjectRestoreReference()
                                        {
                                            ProjectUniqueName = projectReferencePropertyName,
                                            ProjectPath = projectReferenceProjectPath,

                                            IncludeAssets = LibraryIncludeFlagUtils.GetFlags(
                                                flags: includeAssets,
                                                defaultFlags: LibraryIncludeFlags.All),

                                            ExcludeAssets = LibraryIncludeFlagUtils.GetFlags(
                                                flags: excludeAssets,
                                                defaultFlags: LibraryIncludeFlags.None),

                                            PrivateAssets = LibraryIncludeFlagUtils.GetFlags(
                                                flags: privateAssets,
                                                defaultFlags: LibraryIncludeFlagUtils.DefaultSuppressParent),
                                        });
                                    }
                                }
                            }
                            else if (jsonReader.ValueTextEquals(TargetAliasPropertyName))
                            {
                                targetAlias = jsonReader.ReadNextTokenAsString();
                            }
                            else if (jsonReader.ValueTextEquals(FrameworkPropertyName))
                            {
                                frameworkProperty = jsonReader.ReadNextTokenAsString();
                            }
                            else
                            {
                                jsonReader.Skip();
                            }
                        }
                        NuGetFramework framework = frameworkProperty == null ? NuGetFramework.Parse(key) : NuGetFramework.Parse(frameworkProperty);
                        ProjectRestoreMetadataFrameworkInfo frameworkGroup = new(framework)
                        {
                            FrameworkName = framework,
                            TargetAlias = targetAlias,
                            ProjectReferences = projectReferences
                        };

                        targetFrameworks ??= [];
                        targetFrameworks.Add(frameworkGroup);
                    }
                }
            }
            return targetFrameworks;
        }

        private static void ReadTargetFrameworks(PackageSpec packageSpec, ref Utf8JsonStreamReader jsonReader)
        {
            string key = jsonReader.GetString();
            NuGetFramework frameworkName = null;
            bool assetTargetFallback = false;
            Dictionary<string, CentralPackageVersion> centralPackageVersions = null;
            List<LibraryDependency> dependencies = null;
            List<DownloadDependency> downloadDependencies = null;
            HashSet<FrameworkDependency> frameworkReferences = null;
            List<NuGetFramework> imports = null;
            string runtimeIdentifierGraphPath = null;
            string targetAlias = string.Empty;
            bool warn = false;
            Dictionary<string, PrunePackageReference> packagesToPrune = null;

            NuGetFramework secondaryFramework = default;

            if (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.StartObject)
            {
                while (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.PropertyName)
                {
                    if (jsonReader.ValueTextEquals(AssetTargetFallbackPropertyName))
                    {
                        assetTargetFallback = jsonReader.ReadNextTokenAsBoolOrFalse();
                    }
                    else if (jsonReader.ValueTextEquals(SecondaryFrameworkPropertyName))
                    {
                        var secondaryFrameworkString = jsonReader.ReadNextTokenAsString();
                        if (!string.IsNullOrEmpty(secondaryFrameworkString))
                        {
                            secondaryFramework = NuGetFramework.Parse(secondaryFrameworkString);
                        }
                    }
                    else if (jsonReader.ValueTextEquals(CentralPackageVersionsPropertyName))
                    {
                        centralPackageVersions ??= new Dictionary<string, CentralPackageVersion>();
                        ReadCentralPackageVersions(
                            ref jsonReader,
                            centralPackageVersions,
                            packageSpec.FilePath);
                    }
                    else if (jsonReader.ValueTextEquals(DependenciesPropertyName))
                    {
                        dependencies ??= new List<LibraryDependency>();
                        ReadDependencies(
                            ref jsonReader,
                            dependencies,
                            packageSpec.FilePath,
                            isGacOrFrameworkReference: false);
                    }
                    else if (jsonReader.ValueTextEquals(DownloadDependenciesPropertyName))
                    {
                        downloadDependencies ??= new List<DownloadDependency>();
                        ReadDownloadDependencies(
                            ref jsonReader,
                            downloadDependencies,
                            packageSpec.FilePath);
                    }
                    else if (jsonReader.ValueTextEquals(FrameworkAssembliesPropertyName))
                    {
                        dependencies ??= new List<LibraryDependency>();
                        ReadDependencies(
                            ref jsonReader,
                            dependencies,
                            packageSpec.FilePath,
                            isGacOrFrameworkReference: true);
                    }
                    else if (jsonReader.ValueTextEquals(FrameworkReferencesPropertyName))
                    {
                        frameworkReferences ??= new HashSet<FrameworkDependency>();
                        ReadFrameworkReferences(
                            ref jsonReader,
                            frameworkReferences,
                            packageSpec.FilePath);
                    }
                    else if (jsonReader.ValueTextEquals(FrameworkPropertyName))
                    {
                        string frameworkProperty = jsonReader.ReadNextTokenAsString();
                        frameworkName = NuGetFramework.Parse(frameworkProperty);
                    }
                    else if (jsonReader.ValueTextEquals(ImportsPropertyName))
                    {
                        imports ??= new List<NuGetFramework>();
                        ReadImports(packageSpec, ref jsonReader, imports);
                    }
                    else if (jsonReader.ValueTextEquals(RuntimeIdentifierGraphPathPropertyName))
                    {
                        runtimeIdentifierGraphPath = jsonReader.ReadNextTokenAsString();
                    }
                    else if (jsonReader.ValueTextEquals(TargetAliasPropertyName))
                    {
                        targetAlias = jsonReader.ReadNextTokenAsString();
                    }
                    else if (jsonReader.ValueTextEquals(WarnPropertyName))
                    {
                        warn = jsonReader.ReadNextTokenAsBoolOrFalse();
                    }
                    else if (jsonReader.ValueTextEquals(PackagesToPrunePropertyName))
                    {
                        packagesToPrune ??= new Dictionary<string, PrunePackageReference>(StringComparer.OrdinalIgnoreCase);
                        ReadPackagesToPrune(
                            ref jsonReader,
                            packagesToPrune,
                            packageSpec.FilePath);
                    }
                    else
                    {
                        jsonReader.Skip();
                    }
                }
            }

            var targetFrameworkInformation = new TargetFrameworkInformation()
            {
                AssetTargetFallback = assetTargetFallback,
                CentralPackageVersions = centralPackageVersions,
                Dependencies = dependencies != null ? dependencies.ToImmutableArray() : [],
                DownloadDependencies = downloadDependencies != null ? downloadDependencies.ToImmutableArray() : [],
                FrameworkReferences = frameworkReferences,
                Imports = imports != null ? imports.ToImmutableArray() : [],
                RuntimeIdentifierGraphPath = runtimeIdentifierGraphPath,
                PackagesToPrune = packagesToPrune,
                TargetAlias = targetAlias,
                Warn = warn
            };

            if (frameworkName == null) // V3 writers don't set the framework property, so use the key instead.
            {
                frameworkName = NuGetFramework.Parse(key);
            }

            AddTargetFramework(packageSpec, frameworkName, secondaryFramework, targetFrameworkInformation);
        }

        private static HashSet<string> ReadSuppressedAdvisories(ref Utf8JsonStreamReader jsonReader)
        {
            HashSet<string> suppressedAdvisories = null;

            if (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.StartObject)
            {
                while (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.PropertyName)
                {
                    var advisoryUrl = jsonReader.GetString();

                    suppressedAdvisories ??= new HashSet<string>();
                    suppressedAdvisories.Add(advisoryUrl);

                    jsonReader.Skip();
                }
            }

            return suppressedAdvisories;
        }
    }
}