158 lines
5.7 KiB
C#
158 lines
5.7 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Text;
|
|
|
|
namespace UVC.Extention
|
|
{
|
|
/// <summary>
|
|
/// Dictionary 클래스에 대한 확장 메소드를 제공하는 클래스입니다.
|
|
/// </summary>
|
|
public static class DictionaryEx
|
|
{
|
|
/// <summary>
|
|
/// Dictionary를 JSON 문자열로 변환합니다.
|
|
/// 중첩된 Dictionary 객체도 재귀적으로 처리합니다.
|
|
/// </summary>
|
|
/// <typeparam name="TKey">Dictionary의 키 타입</typeparam>
|
|
/// <typeparam name="TValue">Dictionary의 값 타입</typeparam>
|
|
/// <param name="dict">JSON 문자열로 변환할 Dictionary 객체</param>
|
|
/// <returns>JSON 형식의 문자열</returns>
|
|
/// <example>
|
|
/// <code>
|
|
/// var dict = new Dictionary<string, object>
|
|
/// {
|
|
/// { "name", "홍길동" },
|
|
/// { "age", 30 },
|
|
/// { "address", new Dictionary<string, string> { { "city", "서울" }, { "zip", "12345" } } }
|
|
/// };
|
|
///
|
|
/// string json = dict.ToJson();
|
|
/// // 결과: {"name":"홍길동","age":"30","address":{"city":"서울","zip":"12345"}}
|
|
/// </code>
|
|
/// </example>
|
|
public static string ToJson<TKey, TValue>(this IDictionary<TKey, TValue> dict)
|
|
{
|
|
if (dict == null || dict.Count == 0)
|
|
return "{}";
|
|
var sb = new StringBuilder("{");
|
|
foreach (var kvp in dict)
|
|
{
|
|
sb.Append($"\"{kvp.Key}\":");
|
|
|
|
// 값이 null인 경우 처리
|
|
if (kvp.Value == null)
|
|
{
|
|
sb.Append("null,");
|
|
continue;
|
|
}
|
|
|
|
// 값이 중첩된 Dictionary인 경우 재귀적으로 처리
|
|
if (kvp.Value is IDictionary<object, object> dictObject)
|
|
{
|
|
string nestedJson = ToJson(dictObject);
|
|
sb.Append($"{nestedJson},");
|
|
}
|
|
else if (kvp.Value is IDictionary<string, object> dictStringObject)
|
|
{
|
|
string nestedJson = ToJson(dictStringObject);
|
|
sb.Append($"{nestedJson},");
|
|
}
|
|
else if (kvp.Value is IDictionary<string, string> dictStringString)
|
|
{
|
|
string nestedJson = ToJson(dictStringString);
|
|
sb.Append($"{nestedJson},");
|
|
}
|
|
// 값이 숫자인 경우 따옴표 없이 추가
|
|
else if (IsNumericType(kvp.Value.GetType()))
|
|
{
|
|
sb.Append($"{kvp.Value},");
|
|
}
|
|
// 값이 불리언인 경우 따옴표 없이 추가 (소문자 true/false)
|
|
else if (kvp.Value is bool boolValue)
|
|
{
|
|
sb.Append($"{boolValue.ToString().ToLowerInvariant()},");
|
|
}
|
|
// 그 외의 경우 문자열로 처리
|
|
else
|
|
{
|
|
// 특수 문자를 이스케이프 처리
|
|
string escapedValue = EscapeJsonString(kvp.Value.ToString());
|
|
sb.Append($"\"{escapedValue}\",");
|
|
}
|
|
}
|
|
if (dict.Count > 0)
|
|
{
|
|
sb.Length--; // 마지막 쉼표(,) 제거
|
|
}
|
|
sb.Append("}");
|
|
return sb.ToString();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 주어진 타입이 숫자형인지 확인합니다.
|
|
/// </summary>
|
|
/// <param name="type">검사할 타입</param>
|
|
/// <returns>숫자형이면 true, 아니면 false</returns>
|
|
private static bool IsNumericType(Type type)
|
|
{
|
|
if (type == null)
|
|
return false;
|
|
|
|
switch (Type.GetTypeCode(type))
|
|
{
|
|
case TypeCode.Byte:
|
|
case TypeCode.SByte:
|
|
case TypeCode.Int16:
|
|
case TypeCode.Int32:
|
|
case TypeCode.Int64:
|
|
case TypeCode.UInt16:
|
|
case TypeCode.UInt32:
|
|
case TypeCode.UInt64:
|
|
case TypeCode.Single:
|
|
case TypeCode.Double:
|
|
case TypeCode.Decimal:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// JSON 문자열에서 특수 문자를 이스케이프 처리합니다.
|
|
/// </summary>
|
|
/// <param name="value">이스케이프 처리할 문자열</param>
|
|
/// <returns>이스케이프 처리된 문자열</returns>
|
|
private static string EscapeJsonString(string value)
|
|
{
|
|
if (string.IsNullOrEmpty(value))
|
|
return value;
|
|
|
|
StringBuilder sb = new StringBuilder();
|
|
foreach (char c in value)
|
|
{
|
|
switch (c)
|
|
{
|
|
case '\\': sb.Append("\\\\"); break;
|
|
case '\"': sb.Append("\\\""); break;
|
|
case '\n': sb.Append("\\n"); break;
|
|
case '\r': sb.Append("\\r"); break;
|
|
case '\t': sb.Append("\\t"); break;
|
|
case '\b': sb.Append("\\b"); break;
|
|
case '\f': sb.Append("\\f"); break;
|
|
default:
|
|
if (c < 32) // 제어 문자
|
|
{
|
|
sb.Append($"\\u{(int)c:X4}");
|
|
}
|
|
else
|
|
{
|
|
sb.Append(c);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return sb.ToString();
|
|
}
|
|
}
|
|
}
|