File: LockFile\Utf8JsonStreamLockFileConverter.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.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json;
using NuGet.Common;
using NuGet.Frameworks;
using NuGet.Shared;

namespace NuGet.ProjectModel
{
    /// <summary>
    /// A <see cref="IUtf8JsonStreamReaderConverter{T}"/> to allow read JSON into <see cref="LockFile"/>
    /// </summary>
    /// <example>
    /// {
    ///     "version": 3,
    ///     "targets": { <see cref="Utf8JsonStreamLockFileTargetConverter"/> },
    ///     "libraries": { <see cref="Utf8JsonStreamLockFileLibraryConverter"/> },
    ///     "projectFileDependencyGroups": { <see cref="Utf8JsonStreamProjectFileDependencyGroupConverter"/> },
    ///     "packageFolders": { <see cref="Utf8JsonStreamLockFileItemConverter{T}"/> },
    ///     "project": { <see cref="JsonPackageSpecReader.GetPackageSpec(ref Utf8JsonStreamReader, string, string, IEnvironmentVariableReader, string)"/> },
    ///     "logs": [ <see cref="Utf8JsonStreamIAssetsLogMessageConverter"/> ]
    /// }
    /// </example>
    internal class Utf8JsonStreamLockFileConverter : IUtf8JsonStreamReaderConverter<LockFile>
    {
        private static readonly byte[] VersionPropertyName = Encoding.UTF8.GetBytes("version");
        private static readonly byte[] LibrariesPropertyName = Encoding.UTF8.GetBytes("libraries");
        private static readonly byte[] TargetsPropertyName = Encoding.UTF8.GetBytes("targets");
        private static readonly byte[] ProjectFileDependencyGroupsPropertyName = Encoding.UTF8.GetBytes("projectFileDependencyGroups");
        private static readonly byte[] PackageFoldersPropertyName = Encoding.UTF8.GetBytes("packageFolders");
        private static readonly byte[] ProjectPropertyName = Encoding.UTF8.GetBytes("project");
        private static readonly byte[] CentralTransitiveDependencyGroupsPropertyName = Encoding.UTF8.GetBytes("centralTransitiveDependencyGroups");
        private static readonly byte[] LogsPropertyName = Encoding.UTF8.GetBytes("logs");

        public LockFile Read(ref Utf8JsonStreamReader reader)
        {
            return Read(ref reader, LockFileReadFlags.All);
        }

        public static LockFile Read(ref Utf8JsonStreamReader reader, LockFileReadFlags flags)
        {
            if (reader.TokenType != JsonTokenType.StartObject)
            {
                throw new JsonException("Expected StartObject, found " + reader.TokenType);
            }

            var lockFile = new LockFile();

            while (reader.Read() && reader.TokenType == JsonTokenType.PropertyName)
            {
                if (reader.ValueTextEquals(VersionPropertyName))
                {
                    reader.Read();
                    if (reader.TryGetInt32(out int version))
                    {
                        lockFile.Version = version;
                    }
                    else
                    {
                        lockFile.Version = int.MinValue;
                    }
                }
                else if (reader.ValueTextEquals(LibrariesPropertyName))
                {
                    reader.Read();

                    if ((flags & LockFileReadFlags.Libraries) == LockFileReadFlags.Libraries)
                    {
                        lockFile.Libraries = reader.ReadObjectAsList<LockFileLibrary>(Utf8JsonStreamLockFileConverters.LockFileLibraryConverter);
                    }
                    else
                    {
                        reader.Skip();
                        lockFile.Libraries = Array.Empty<LockFileLibrary>();
                    }
                }
                else if (reader.ValueTextEquals(TargetsPropertyName))
                {
                    reader.Read();

                    if ((flags & LockFileReadFlags.Targets) == LockFileReadFlags.Targets)
                    {
                        if (lockFile.Version >= 4)
                        {
                            lockFile.Targets = reader.ReadObjectAsList<LockFileTarget>(Utf8JsonStreamLockFileConverters.LockFileTargetConverterV4);
                        }
                        else
                        {
                            lockFile.Targets = reader.ReadObjectAsList<LockFileTarget>(Utf8JsonStreamLockFileConverters.LockFileTargetConverter);
                        }
                    }
                    else
                    {
                        reader.Skip();
                        lockFile.Targets = Array.Empty<LockFileTarget>();
                    }
                }
                else if (reader.ValueTextEquals(ProjectFileDependencyGroupsPropertyName))
                {
                    reader.Read();

                    if ((flags & LockFileReadFlags.ProjectFileDependencyGroups) == LockFileReadFlags.ProjectFileDependencyGroups)
                    {
                        lockFile.ProjectFileDependencyGroups = reader.ReadObjectAsList<ProjectFileDependencyGroup>(Utf8JsonStreamLockFileConverters.ProjectFileDepencencyGroupConverter);
                    }
                    else
                    {
                        reader.Skip();
                        lockFile.ProjectFileDependencyGroups = Array.Empty<ProjectFileDependencyGroup>();
                    }
                }
                else if (reader.ValueTextEquals(PackageFoldersPropertyName))
                {
                    reader.Read();

                    if ((flags & LockFileReadFlags.PackageFolders) == LockFileReadFlags.PackageFolders)
                    {
                        lockFile.PackageFolders = reader.ReadObjectAsList<LockFileItem>(Utf8JsonStreamLockFileConverters.LockFileItemConverter);
                    }
                    else
                    {
                        reader.Skip();
                        lockFile.PackageFolders = Array.Empty<LockFileItem>();
                    }
                }
                else if (reader.ValueTextEquals(ProjectPropertyName))
                {
                    reader.Read();

                    if ((flags & LockFileReadFlags.PackageSpec) == LockFileReadFlags.PackageSpec)
                    {
                        lockFile.PackageSpec = JsonPackageSpecReader.GetPackageSpec(
                            ref reader,
                            name: null,
                            packageSpecPath: null,
                            EnvironmentVariableWrapper.Instance,
                            snapshotValue: null);
                    }
                    else
                    {
                        reader.Skip();
                        lockFile.PackageSpec = new PackageSpec(Array.Empty<TargetFrameworkInformation>());
                    }
                }
                else if (reader.ValueTextEquals(CentralTransitiveDependencyGroupsPropertyName))
                {
                    IList<CentralTransitiveDependencyGroup> results = null;
                    if ((flags & LockFileReadFlags.CentralTransitiveDependencyGroups) == LockFileReadFlags.CentralTransitiveDependencyGroups)
                    {
                        if (reader.Read() && reader.TokenType == JsonTokenType.StartObject)
                        {
                            while (reader.Read() && reader.TokenType == JsonTokenType.PropertyName)
                            {
                                results ??= new List<CentralTransitiveDependencyGroup>();
                                var frameworkPropertyName = reader.GetString();
                                NuGetFramework framework = NuGetFramework.Parse(frameworkPropertyName);

                                JsonPackageSpecReader.ReadCentralTransitiveDependencyGroup(
                                    jsonReader: ref reader,
                                    results: out var dependencies,
                                    packageSpecPath: string.Empty);
                                results.Add(new CentralTransitiveDependencyGroup(framework, dependencies));
                            }
                        }
                    }
                    else
                    {
                        reader.Skip();
                    }

                    lockFile.CentralTransitiveDependencyGroups = results ?? Array.Empty<CentralTransitiveDependencyGroup>();
                }
                else if (reader.ValueTextEquals(LogsPropertyName))
                {
                    reader.Read();
                    if ((flags & LockFileReadFlags.LogMessages) == LockFileReadFlags.LogMessages)
                    {
                        lockFile.LogMessages = reader.ReadListOfObjects<IAssetsLogMessage>(Utf8JsonStreamLockFileConverters.IAssetsLogMessageConverter);
                    }
                    else
                    {
                        reader.Skip();
                        lockFile.LogMessages = Array.Empty<IAssetsLogMessage>();
                    }
                }
                else
                {
                    reader.Skip();
                }
            }

            if (lockFile.Version == LockFileFormat.LegacyVersion)
            {
                // Populate the alias at read time. This allows readers to use the alias to find targets regardless of what the underlying assets file format is.
                foreach (var target in lockFile.Targets.NoAllocEnumerate())
                {
                    target.TargetAlias = lockFile.PackageSpec?.GetTargetFramework(target.TargetFramework)?.TargetAlias;
                }
            }

            if (lockFile.Version >= LockFileFormat.AliasedVersion)
            {
                foreach (var target in lockFile.Targets.NoAllocEnumerate())
                {
                    target.TargetFramework = lockFile.PackageSpec.GetTargetFramework(target.TargetAlias).FrameworkName;
                }
            }

            var projectPath = lockFile.PackageSpec?.RestoreMetadata?.ProjectPath;
            if (!string.IsNullOrEmpty(projectPath) && lockFile.LogMessages.Count > 0)
            {
                foreach (AssetsLogMessage message in lockFile.LogMessages.Where(x => string.IsNullOrEmpty(x.ProjectPath)))
                {
                    message.ProjectPath = projectPath;
                    message.FilePath = projectPath;
                }
            }

            return lockFile;
        }
    }
}