プロジェクトの都合上、古いバージョンで新しいライブラリなど入れることができないが、jsonファイルで通信のやりとりを行う必要があるため独自にコーディングする機会があったので備忘録として記載する。
ソースコード
配列やDateTime、クラスにも対応したものが以下のコードになります。
using System.Text;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Collections;
namespace clientForm
{
internal class JsonSerializer
{
public static T Parse<T>(string json) where T : new()
{
var result = ParseObject(typeof(T), json);
return (T)result;
}
private static object ParseObject(Type type, string json)
{
object obj = Activator.CreateInstance(type);
var properties = type.GetProperties(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public);
foreach (var prop in properties)
{
if (!prop.CanWrite) continue;
string pattern = $"\"{prop.Name}\"\\s*:\\s*(\".*?\"|\\d+|\\[.*?\\]|{{.*?}})";
Match match = Regex.Match(json, pattern);
if (!match.Success) continue;
string rawValue = TrimParseValue(match.Groups[1].Value);
object value = ParsePropertyValue(prop.PropertyType, rawValue);
prop.SetValue(obj, value);
}
return obj;
}
/// <summary>
/// 対応する型にキャスト
/// </summary>
public static object ParsePropertyValue(Type type, string rawValue)
{
object value = null;
if (type == typeof(string))
value = TrimParseValue(rawValue);
else if (type == typeof(int) || type == typeof(int?))
value = ParseInt(rawValue);
else if (type == typeof(float) || type == typeof(float?))
value = ParseFloat(rawValue);
else if (type == typeof(bool) || type == typeof(bool?))
value = rawValue.ToLower().Contains("true");
else if (type == typeof(DateTime) || type == typeof(DateTime?))
value = ParseDateTime(rawValue);
else if (type.IsArray)
{
var elementType = type.GetElementType();
value = ParseArray(elementType, rawValue);
}
else if (type.IsClass)
value = ParseObject(type, rawValue);
return value;
}
/// <summary>
/// 前後の空白と"を削除
/// </summary>
private static string TrimParseValue(string value)
{
return value.Trim().Trim('"');
}
private static object ParseArray(Type elementType, string raw)
{
var itemPattern = "\"(.*?)\"|\\d+|true|false|\\{.*?\\}";
var matched = Regex.Matches(raw, itemPattern);
var list = new List<object>();
foreach (Match match in matched)
{
string item = match.Value.Trim();
object value = ParsePropertyValue(elementType, item);
list.Add(value);
}
var array = Array.CreateInstance(elementType, list.Count);
for (int i = 0; i < list.Count; i++)
{
array.SetValue(list[i], i);
}
return array;
}
private static int? ParseInt(string value)
{
if (string.IsNullOrEmpty(value)) return null;
if (int.TryParse(value, out int result)) return result;
return null;
}
private static float? ParseFloat(string value)
{
if (string.IsNullOrEmpty(value)) return null;
if (float.TryParse(value, out float result)) return result;
return null;
}
private static DateTime? ParseDateTime(string value)
{
if (string.IsNullOrEmpty(value)) return null;
if (DateTime.TryParse(value, out DateTime result)) return result;
return null;
}
public static string ToJson(object obj)
{
if (obj == null) return "null";
Type type = obj.GetType();
var properties = type.GetProperties();
StringBuilder json = new StringBuilder();
json.Append("{");
for (int i = 0; i < properties.Length; i++)
{
var prop = properties[i];
var value = prop.GetValue(obj, null);
string formattedValue = FormatValue(value);
json.Append($"\"{prop.Name}\":{formattedValue}");
if (i < properties.Length - 1)
{
json.Append(",");
}
}
json.Append("}");
return json.ToString();
}
private static string FormatValue(object value)
{
if (value == null) return "null";
if (value is string || value is char)
{
return $"\"{value}\"";
}
else if (value is bool)
{
return value.ToString().ToLower();
}
else if (value is int || value is float)
{
return value.ToString();
}
else if (value is DateTime)
{
DateTime dt = (DateTime)value;
return $"\"{dt:yyyy-MM-ddTHH:mm:ss}\"";
}
else if (value is IEnumerable)
{
var enumerable = (IEnumerable)value;
StringBuilder arrayJson = new StringBuilder();
arrayJson.Append("[");
bool first = true;
foreach (var item in enumerable)
{
if (!first)
{
arrayJson.Append(",");
}
arrayJson.Append(FormatValue(item));
first = false;
}
arrayJson.Append("]");
return arrayJson.ToString();
}
// ネストされたクラス対応(再帰)
return ToJson(value);
}
}
}