Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

[C#]DataContractJsonSerializerのJsonを読みやすく整形する

More than 3 years have passed since last update.

これはなに

C#でDataContractJsonSerializerを使いJsonを出力すると、1行になって出力されるので、
最低限見やすいように整形するコードです。

コード

json.cs
public class Json
{
    public static string Serialize(object target)
    {
        using (var stream = new MemoryStream())
        {
            var serializer = new DataContractJsonSerializer(target.GetType());
            serializer.WriteObject(stream, target);
            return Encoding.UTF8.GetString(stream.ToArray());
        }
    }

    //2017/04/17 StringBuilder版
    public static string ToReadable(string json)
    {
        if (string.IsNullOrWhiteSpace(json)) return json;
        int i = 0;
        int indent = 0;
        int quoteCount = 0;
        int position = -1;
        var sb = new StringBuilder();
        int lastindex = 0;
        while (true)
        {
            if (i > 0 && json[i] == '"' && json[i - 1] != '\\') quoteCount++;

            if (quoteCount % 2 == 0) //is not value(quoted)
            {
                if (json[i] == '{' || json[i] == '[')
                {
                    indent++;
                    position = 1;
                }
                else if (json[i] == '}' || json[i] == ']')
                {
                    indent--;
                    position = 0;
                }
                else if (json.Length > i && json[i] == ',' && json[i + 1] == '"')
                {
                    position = 1;
                }
                if (position >= 0)
                {
                    sb.AppendLine(json.Substring(lastindex, i + position - lastindex));
                    sb.Append(new string(' ', indent * 4));
                    lastindex = i + position;
                    position = -1;
                }
            }

            i++;
            if (json.Length <= i)
            {
                sb.Append(json.Substring(lastindex));
                break;
            }

        }
        return sb.ToString();
    }

    public static string ToReadableOld(string json)
    {
        if (string.IsNullOrWhiteSpace(json)) return json;
        int i = 0;
        int indent = 0;
        int quoteCount = 0;
        int position = -1;
        string indentStr = "\n";
        while (true)
        {
            if (i > 0 && json[i] == '"' && json[i - 1] != '\\') quoteCount++;

            if (quoteCount % 2 == 0) //is not value(quoted)
            {
                if (json[i] == '{' || json[i] == '[')
                {
                    indent++;
                    position = 1;
                }
                else if (json[i] == '}' || json[i] == ']')
                {
                    indent--;
                    position = 0;
                }
                else if (json.Length > i && json[i] == ',' && json[i + 1] == '"')
                {
                    position = 1;
                }
                if (position >= 0)
                {
                    indentStr = "\n" + new string(' ', indent * 4);
                    json = json.Substring(0, i + position) + indentStr + json.Substring(i + position);
                    i += indentStr.Length - position;
                    position = -1;
                }
            }

            i++;
            if (json.Length <= i) break;
        }
        return json;
    }

    public static T Deserialize<T>(string json)
    {
        using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(json)))
        {
            var serializer = new DataContractJsonSerializer(typeof(T));
            return (T)serializer.ReadObject(stream);
        }
    }

    public static object Deserialize(Type type, string json)
    {
        using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(json)))
        {
            var serializer = new DataContractJsonSerializer(type);
            return serializer.ReadObject(stream);
        }
    }
}

サンプル

sample.cs
public class sample
{
    public int num { get; set; } = 100;
    public string str { get; set; } = "abc";
    public List<string> list { get; set; } = new List<string> { "A", "B", "C", "Escape:", "{", "}", "\"", "[", "]" };
}

private void test()
{
    var s = new sample();

    var json1 = Json.Serialize(s);
    Debug.WriteLine(json1);
    //{"list":["A","B","C","Escape:","{","}","\"","[","]"],"num":100,"str":"abc"}

    var json2 = Json.ToReadable(json1);
    Debug.WriteLine(json2);
    //{
    //    "list":[
    //        "A",
    //        "B",
    //        "C",
    //        "Escape:",
    //        "{",
    //        "}",
    //        "\"",
    //        "[",
    //        "]"
    //    ],
    //    "num":100,
    //    "str":"abc"
    //}

    var s1 = Json.Deserialize<sample>(json1);
    var s2 = Json.Deserialize<sample>(json2);
    Debug.WriteLine(Json.Serialize(s1) == Json.Serialize(s2));
    //True

}

おわりに

とりあえず今のところ使っていて問題は起きていませんが、
ご指摘あればどんどんお願いします。

追記 2017/04/17

大きいJsonを処理した際あまりに遅かったため、StringBuilderを使用したコードに
書き換えました。ToReadableが新しいコードでToReadableOldが古いコードです。
改行が\nから\r\nになっています。

test.cs
var j = Json.Serializer.Serialize(s);
var sw = new System.Diagnostics.Stopwatch();
sw.Start();
var t1 = Json.Serializer.ToReadableOld(j);
Debug.Log("Old:" + sw.ElapsedMilliseconds.ToString() + "ms");

sw.Restart();
var t2 = Json.Serializer.ToReadable(j).Replace("\r","");
Debug.Log("New:" + sw.ElapsedMilliseconds.ToString() + "ms");

Debug.Log("Old == New:" + (t1 == t2).ToString());
//Old:140653ms
//New:31ms
//Old == New:True
sh_akira
Win/iOS/Android/Linux/組込 C/C#/Xamarin.Forms/Unity VR機器でVRMの3Dモデルをコントロール!バーチャルモーションキャプチャーの開発をしています 特に明示されていない場合、記事中のソースコードはパブリックドメインです
https://sh-akira.github.io/VirtualMotionCapture/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away