API/Autodoc/Files/BaseJsonSerializer.cs.en.md


Namespaces

Name

Classes

Name
class RuStore::UnityInstallReferrer::Internal::BaseJsonSerializer

Source code

using RuStore.UnityInstallReferrer.Model;
using System;
using System.Collections;
using System.Reflection;
using System.Text;

namespace RuStore.UnityInstallReferrer.Internal {

    public class BaseJsonSerializer : IJsonSerializer {

        public string Serialize<T>(T value, bool prettyPrint = false) =>
            Serialize(value, prettyPrint, 0);

        private string Serialize(object obj, bool prettyPrint, int indentLevel) {
            if (obj == null) return "null";

            if (obj is string str) return $"\"{EscapeJsonString(str)}\"";
            if (obj is Enum enumValue) return $"\"{ToCamelCase(enumValue.ToString())}\"";
            if (IsNumeric(obj)) return obj.ToString();
            if (obj is bool boolValue) return boolValue.ToString().ToLower();
            if (obj is ICollection collection) return SerializeCollection(collection, prettyPrint, indentLevel);
            if (IsBaseValue(obj)) return SerializeBaseValue(obj);
            if (obj is DateTime) return SerializeDateTime(obj);

            return SerializeObject(obj, prettyPrint, indentLevel);
        }

        private string SerializeDateTime(object obj) {
            var dateString = (obj as DateTime?)?.ToString("yyyy-MM-ddTHH:mm:ss.fffZ");
            return $"\"{dateString}\"";
        }

        private string SerializeCollection(ICollection collection, bool prettyPrint, int indentLevel) {
            if (collection.Count == 0) return "[]";

            var sb = new StringBuilder();
            sb.Append("[");

            string indent = prettyPrint ? GetIndent(indentLevel + 1) : "";
            bool first = true;

            foreach (object item in collection) {
                if (!first) sb.Append(",");
                first = false;

                if (prettyPrint) {
                    sb.AppendLine();
                    sb.Append(indent);
                }

                sb.Append(Serialize(item, prettyPrint, indentLevel + 1));
            }

            if (prettyPrint) {
                sb.AppendLine();
                sb.Append(GetIndent(indentLevel));
            }

            sb.Append("]");

            return sb.ToString();
        }

        private string SerializeObject(object obj, bool prettyPrint, int indentLevel) {
            Type type = obj.GetType();
            var sb = new StringBuilder();
            sb.Append("{");

            PropertyInfo[] properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
            
            // Для вложенных классов добавляем поле "type" с именем типа
            bool isNested = type.IsNested;
            if (!isNested && properties.Length == 0) return "{}";

            string indent = prettyPrint ? GetIndent(indentLevel + 1) : "";
            bool first = true;

            if (isNested) {
                if (prettyPrint) {
                    sb.AppendLine();
                    sb.Append(indent);
                }
                sb.Append($"\"type\":\"{type.Name}\"");
                first = false;
            }

            foreach (PropertyInfo prop in properties) {
                object value = prop.GetValue(obj);
                
                // Пропускаем null значения и пустые строки
                if (value == null || (value is string strValue && string.IsNullOrEmpty(strValue))) {
                    continue;
                }

                if (!first) sb.Append(",");
                first = false;

                if (prettyPrint) {
                    sb.AppendLine();
                    sb.Append(indent);
                }

                sb.Append($"\"{prop.Name}\":");
                if (prettyPrint) sb.Append(" ");

                sb.Append(Serialize(value, prettyPrint, indentLevel + 1));
            }

            if (prettyPrint) {
                sb.AppendLine();
                sb.Append(GetIndent(indentLevel));
            }

            sb.Append("}");

            return sb.ToString();
        }

        private string SerializeBaseValue(object obj) {
            // Сериализуем BaseValue<T> как примитив (значение свойства value)
            Type type = obj.GetType();
            PropertyInfo valueProperty = type.GetProperty("value");
            if (valueProperty != null) {
                object value = valueProperty.GetValue(obj);
                return Serialize(value, false, 0);
            }
            return SerializeObject(obj, false, 0);
        }

        private bool IsNumeric(object value) =>
            value is int or long or float or double or decimal;

        private static bool IsBaseValue(object obj) {
            if (obj == null) return false;
            Type type = obj.GetType();
            while (type != null) {
                if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(BaseValue<>))
                    return true;
                type = type.BaseType;
            }

            return false;
        }

        private static string GetIndent(int level) =>
            new string(' ', level * 4);

        private static string ToCamelCase(string value) {
            if (string.IsNullOrEmpty(value)) return value;
            return char.ToLowerInvariant(value[0]) + value.Substring(1);
        }

        private static string EscapeJsonString(string value) {
            if (string.IsNullOrEmpty(value)) return value;
            return value
                .Replace("\\", "\\\\")
                .Replace("\"", "\\\"")
                .Replace("\n", "\\n")
                .Replace("\r", "\\r")
                .Replace("\t", "\\t");
        }
    }
}