File: SdkDirectoryWorkloadManifestProvider.GlobalJsonReader.cs
Web Access
Project: src\src\sdk\src\Resolvers\Microsoft.NET.Sdk.WorkloadManifestReader\Microsoft.NET.Sdk.WorkloadManifestReader.csproj (Microsoft.NET.Sdk.WorkloadManifestReader)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Text;
using System.Text.Json;
using Microsoft.NET.Sdk.Localization;
using static Microsoft.NET.Sdk.WorkloadManifestReader.WorkloadManifestReader;

namespace Microsoft.NET.Sdk.WorkloadManifestReader
{
    public partial class SdkDirectoryWorkloadManifestProvider
    {
        public static class GlobalJsonReader
        {
            public static string? GetWorkloadVersionFromGlobalJson(string? globalJsonPath, out bool? shouldUseWorkloadSets)
            {
                shouldUseWorkloadSets = null;
                if (string.IsNullOrEmpty(globalJsonPath))
                {
                    return null;
                }

                var readerOptions = new JsonReaderOptions
                {
                    AllowTrailingCommas = true,
                    CommentHandling = JsonCommentHandling.Skip
                };

                // Use StreamReader with BOM detection to determine the encoding
                using var streamReader = new StreamReader(globalJsonPath, detectEncodingFromByteOrderMarks: true);
                streamReader.Peek(); // trigger BOM detection without consuming content

                if (streamReader.CurrentEncoding is UTF8Encoding)
                {
                    // UTF-8 (with or without BOM): stream the underlying file directly.
                    // Utf8JsonStreamReader handles the UTF-8 BOM itself.
                    streamReader.BaseStream.Seek(0, SeekOrigin.Begin);
                    var reader = new Utf8JsonStreamReader(streamReader.BaseStream, readerOptions);
                    return ParseGlobalJson(ref reader, out shouldUseWorkloadSets);
                }
                else
                {
                    // For other encodings (e.g. UTF-16 LE/BE), transcode to UTF-8 in memory.
                    // global.json files are small so this is acceptable.
                    var content = streamReader.ReadToEnd();
                    using var memStream = new MemoryStream(Encoding.UTF8.GetBytes(content));
                    var reader = new Utf8JsonStreamReader(memStream, readerOptions);
                    return ParseGlobalJson(ref reader, out shouldUseWorkloadSets);
                }
            }

            private static string? ParseGlobalJson(ref Utf8JsonStreamReader reader, out bool? shouldUseWorkloadSets)
            {
                shouldUseWorkloadSets = null;
                string? workloadVersion = null;

                JsonReader.ConsumeToken(ref reader, JsonTokenType.StartObject);
                while (reader.Read())
                {
                    switch (reader.TokenType)
                    {
                        case JsonTokenType.PropertyName:
                            var propName = reader.GetString();
                            if (string.Equals("sdk", propName, StringComparison.OrdinalIgnoreCase))
                            {
                                JsonReader.ConsumeToken(ref reader, JsonTokenType.StartObject);

                                bool readingSdk = true;
                                while (readingSdk && reader.Read())
                                {
                                    switch (reader.TokenType)
                                    {
                                        case JsonTokenType.PropertyName:
                                            var sdkPropName = reader.GetString();
                                            if (string.Equals("workloadVersion", sdkPropName, StringComparison.OrdinalIgnoreCase))
                                            {
                                                workloadVersion = JsonReader.ReadString(ref reader);
                                            }
                                            else if (string.Equals("workloads-update-mode", sdkPropName, StringComparison.OrdinalIgnoreCase))
                                            {
                                                var useWorkloadSetsString = JsonReader.ReadString(ref reader);
                                                shouldUseWorkloadSets = "workload-set".Equals(useWorkloadSetsString, StringComparison.OrdinalIgnoreCase) ? true :
                                                                        "manifests".Equals(useWorkloadSetsString, StringComparison.OrdinalIgnoreCase) ? false :
                                                                        shouldUseWorkloadSets;
                                            }
                                            else
                                            {
                                                JsonReader.ConsumeValue(ref reader);
                                            }
                                            break;
                                        case JsonTokenType.EndObject:
                                            readingSdk = false;
                                            break;
                                        default:
                                            throw new JsonFormatException(Strings.UnexpectedTokenAtOffset, reader.TokenType, reader.TokenStartIndex);
                                    }
                                }
                            }
                            else
                            {
                                JsonReader.ConsumeValue(ref reader);
                            }
                            break;

                        case JsonTokenType.EndObject:
                            return workloadVersion;
                        default:
                            throw new JsonFormatException(Strings.UnexpectedTokenAtOffset, reader.TokenType, reader.TokenStartIndex);
                    }
                }

                throw new JsonFormatException(Strings.IncompleteDocument);
            }
        }
    }
}