// 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); } } } } |