using System;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.Build.Framework;
namespace AggregateConfigBuildTask
{
internal static class JsonHelper
{
///
/// Converts a dictionary of string and JsonElement pairs to a JsonElement.
///
/// A dictionary containing string keys and JsonElement values.
/// A JsonElement representing the dictionary.
public static Task ConvertToJsonElement(Dictionary dictionary)
{
return ConvertObjectToJsonElement(dictionary);
}
///
/// Converts a list of JsonElements to a JsonElement.
///
/// A list containing JsonElement objects.
/// A JsonElement representing the list.
public static Task ConvertToJsonElement(List list)
{
return ConvertObjectToJsonElement(list);
}
///
/// Converts dictionary keys to strings recursively for any dictionary or list.
///
/// The object to process, can be a dictionary or list.
/// An object where dictionary keys are converted to strings.
public static object ConvertKeysToString(object data)
{
if (data is IDictionary dict)
{
var convertedDict = new Dictionary(StringComparer.Ordinal);
foreach (var key in dict.Keys)
{
var stringKey = key.ToString(); // Ensure the key is a string
convertedDict[stringKey] = ConvertKeysToString(dict[key]); // Recursively convert values
}
return convertedDict;
}
else if (data is IList list)
{
var convertedList = new List();
foreach (var item in list)
{
convertedList.Add(ConvertKeysToString(item)); // Recursively convert list items
}
return convertedList;
}
return data; // Return the item as-is if it's not a dictionary or list
}
///
/// Parses an array of key-value pairs from an array.
/// Prioritizes the 'Value' metadata when provided. Falls back to legacy format "key=value" if no 'Value' is present.
/// Supports escaping of the '=' sign using '\='.
///
/// An array of key-value pairs in the form of ITaskItem[] or legacy format strings.
/// A dictionary containing the parsed key-value pairs.
public static Dictionary ParseAdditionalProperties(ITaskItem[] properties)
{
var additionalPropertiesDict = new Dictionary(StringComparer.Ordinal);
const string unicodeEscape = "\u001F";
char[] split = new[] { '=' };
if (properties != null)
{
foreach (var property in properties)
{
// Check if the new 'Value' metadata is present
string key = property.ItemSpec;
string value = property.GetMetadata("Value");
if (!string.IsNullOrEmpty(value))
{
// Use the new method if the 'Value' metadata is provided
additionalPropertiesDict[key] = value;
continue;
}
// Fallback to legacy parsing if no 'Value' metadata is provided and key contains '='
var sanitizedProperty = key.Replace(@"\=", unicodeEscape);
var keyValue = sanitizedProperty.Split(split, 2);
if (keyValue.Length == 2)
{
additionalPropertiesDict[keyValue[0].Replace(unicodeEscape, "=")] = keyValue[1].Replace(unicodeEscape, "=");
}
}
}
return additionalPropertiesDict;
}
///
/// Converts any object to a JsonElement.
///
/// The object to convert to JsonElement.
/// A JsonElement representing the object.
/// is null .
public static async Task ConvertObjectToJsonElement(object value)
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
using (var memoryStream = new MemoryStream())
{
await JsonSerializer.SerializeAsync(memoryStream, value).ConfigureAwait(false);
memoryStream.Seek(0, SeekOrigin.Begin);
using (var jsonDocument = await JsonDocument.ParseAsync(memoryStream).ConfigureAwait(false))
{
return jsonDocument.RootElement.Clone();
}
}
}
///
/// Converts a JsonElement to a dictionary of string and JsonElement pairs.
///
/// The JsonElement to convert.
/// A dictionary representing the JsonElement.
public static Dictionary JsonElementToDictionary(JsonElement jsonElement)
{
var dictionary = new Dictionary(StringComparer.Ordinal);
foreach (var property in jsonElement.EnumerateObject())
{
dictionary.Add(property.Name, property.Value);
}
return dictionary;
}
///
/// Converts a JsonElement to an appropriate object representation, such as a dictionary, list, or primitive.
///
/// The JsonElement to convert.
/// An object representing the JsonElement.
/// Thrown when we fail to find an .NET type for the .
public static object ConvertJsonElementToObject(JsonElement element)
{
switch (element.ValueKind)
{
case JsonValueKind.Object:
return JsonElementToDictionary(element);
case JsonValueKind.Array:
var list = new List();
foreach (var item in element.EnumerateArray())
{
list.Add(ConvertJsonElementToObject(item));
}
return list;
case JsonValueKind.String:
return element.GetString();
case JsonValueKind.Number:
return element.TryGetInt64(out long l) ? l : element.GetDouble();
case JsonValueKind.True:
case JsonValueKind.False:
return element.GetBoolean();
case JsonValueKind.Null:
return null;
default:
throw new InvalidOperationException($"Unsupported JsonValueKind: {element.ValueKind}");
}
}
}
}