using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; using System.Collections; using System.Collections.Generic; using System.Reflection; namespace Tango.Telemetry.Helpers { internal static class JsonFlattener { public static string FlattenObjectToFlatJson(object obj, Formatting format) { var flat = new JObject(); FlattenRecursive(obj, flat, prefix: null); return flat.ToString(format); } private static void FlattenRecursive(object obj, JObject target, string prefix) { if (obj == null) return; var type = obj.GetType(); if (type == typeof(JObject)) { foreach (var prop in ((JObject)obj).Properties()) { FlattenRecursive(prop.Value, target, Combine(prefix, prop.Name)); } return; } if (obj is JValue jVal) { target[Combine(prefix, "Value")] = JToken.FromObject(jVal.Value); return; } if (obj is JToken jToken && jToken.Type == JTokenType.Object) { foreach (var prop in ((JObject)jToken).Properties()) { FlattenRecursive(prop.Value, target, Combine(prefix, prop.Name)); } return; } foreach (var prop in type.GetProperties(BindingFlags.Public | BindingFlags.Instance)) { if (!prop.CanRead) continue; var value = prop.GetValue(obj); if (value == null) continue; var valueType = value.GetType(); if (IsSimpleType(valueType)) { target[Combine(prefix, prop.Name)] = JToken.FromObject(value); } else if (value is IEnumerable enumerable && !(value is string)) { int index = 0; foreach (var item in enumerable) { FlattenRecursive(item, target, Combine(prefix, $"{prop.Name}_{index}")); index++; } } else { FlattenRecursive(value, target, Combine(prefix, prop.Name)); } } } private static string Combine(string prefix, string name) { return string.IsNullOrEmpty(prefix) ? name : $"{prefix}_{name}"; } private static bool IsSimpleType(Type type) { return type.IsPrimitive || type.IsValueType || type == typeof(string); } } }