解析JSON、擴展Fiddler


解析JSON、擴展Fiddler

按文章結構,這部分應該給出WCFRest項目示例,我想WinForm示例足夠詳盡了,況且WCFRest還不需要使用插件AppDomain那一套,於是把最近寫的Fiddler擴展搬上來吧。

Fiddler有一套自成的插件系統,可以在其官方網站找到完整文檔(戳這里)。通過其提供的一整套接口,我們可以從界面至功能全方位擴展它。這里主題簡單,我們只為其添加一個JSON解析界面。

PART I:JSON解析

Mgen有一個JSON解析范例(戳這里)代碼相當好看,WPF模塊綁定也很強大。這里使用Json.com的一個示例稍作修改,解析效果如下:

上述代碼過於忠實地體現了Newtonsoft的設計,只有JValue被解析成葉節點,其他JToken對象全部是枝節點,解析出來的JSON樹層次太深可讀性不夠好。

Newtonsoft中JSON結構:

這里重新設計結構圖如下:

一共有4種節點:指示值的JsonValueNode葉節點、指示鍵值對的JsonPropertyNode的葉節點,指示數組的JsonArrayNode枝節點,及指示對象的JsonObjectNode的樹節點。解析時關鍵在對JProperty的處理:把Value為JValue的JProperty對象解析成JsonPropertyNode葉節點,Value為JContainer的JProperty視子節點屬性實例成JsonArrayNode或JsonObjectNode樹節點,初步代碼如下:

復制代碼
public class JsonNode
{
    public IEnumerable<JsonNode> Children { get; internal set; }

    internal JsonNode()
    {
    }
}

public class JsonValueNode : JsonNode
{
    public Object Value { get; private set; }

    public JsonValueNode(Object value)
    {
        Value = value;
    }

    public override String ToString()
    {
        return Value != null ? Value.ToString() : "<null>";
    }
}

public class JsonPropertyNode : JsonNode
{
    public String Name { get; private set; }
    public Object Value { get; private set; }

    public JsonPropertyNode(String name, Object value)
    {
        Name = name;
        Value = value;
    }

    public override String ToString()
    {
        return String.Concat(Name, " : ", (Value != null ? Value.ToString() : "<null>"));
    }
}

public class JsonArrayNode : JsonNode
{
    public String Name { get; private set; }

    public JsonArrayNode(String name)
    {
        Name = name ?? "[]";
    }

    public override String ToString()
    {
        return Name;
    }
}

public class JsonObjectNode : JsonNode
{
    public String Name { get; private set; }

    public JsonObjectNode(String name)
    {
        Name = name ?? "{}";
    }

    public override String ToString()
    {
        return Name;
    }
}
復制代碼

JsonNodeFactory作為JsonNode的建造者:

復制代碼
public class JsonNodeFactory
{
    public static JsonNode CreateFromJToken(JToken jtoken)
    {
        if (jtoken is JValue)
        {
            return new JsonValueNode(((JValue)jtoken).Value);
        }
        else if (jtoken is JProperty)
        {
            JProperty jproperty = (JProperty)jtoken;
            if (jproperty.Value is JValue)
            {
                return new JsonPropertyNode(jproperty.Name, ((JValue)jproperty.Value).Value);
            }
            else if (jproperty.Value is JArray)
            {
                JsonArrayNode jsonNode = new JsonArrayNode(jproperty.Name);
                jsonNode.Children = ((JArray)jproperty.Value).Children().Select(n => CreateFromJToken(n));
                return jsonNode;
            }
            else if (jproperty.Value is JObject)
            {
                JsonObjectNode jsonNode = new JsonObjectNode(jproperty.Name);
                jsonNode.Children = ((JObject)jproperty.Value).Children().Select(n => CreateFromJToken(n));
                return jsonNode;
            }
            else
            {
                throw new Exception("Unknown JProperty");
            }
        }
        else if (jtoken is JArray)
        {
            JsonArrayNode jsonNode = new JsonArrayNode(null);
            jsonNode.Children = ((JArray)jtoken).Children().Select(n => CreateFromJToken(n));
            return jsonNode;
        }
        else if (jtoken is JObject)
        {
            JsonObjectNode jsonNode = new JsonObjectNode(null);
            jsonNode.Children = ((JObject)jtoken).Children().Select(n => CreateFromJToken(n));
            return jsonNode;
        }
        else
        {
            throw new Exception("Unknown jtoken");
        }
    }
}
復制代碼

文章后面的代碼文件中更具體的實現加入了父節點與索引,ToString()邏輯更完備。客戶端調用:

復制代碼
class Program
{
    static void Main(string[] args)
    {
        JToken jtoken = JToken.Parse(System.IO.File.ReadAllText("json.txt"));
        JsonNode node = JsonNodeFactory.CreateFromJToken(jtoken);
        Debug.Listeners.Add(new TextWriterTraceListener(Console.Out));
        Display(node);
    }

    private static void Display(JsonNode node)
    {
        Debug.WriteLine(node);
        if (node.Children != null)
        {
            Debug.Indent();
            foreach (JsonNode sub in node.Children)
            {
                Display(sub);
            }
            Debug.Unindent();
        }
    }
}
復制代碼

json.txt見截圖與后文代碼文件,新的解析結構:

 

PART2:Fiddler插件

Fiddler自帶的JSON顯示是一個簡單的Tree,無法完成復雜功能。Codeplex上有一個JsonView項目(戳這里),Fiddler子項目丟到%Program Files%/Fiddler/Inspectors目錄即可。問題在於它有BUG,且使用Newtonsoft的Json.Net版本極其低。沒辦法,重寫一個。這里有2處需要注意:

1. 需要添加Public類,實現Inspector2、IResponseInspector2,抽象類Inspector2.AddToTab(TabPage o)是UI呈現方法,headers與body屬性內部可以完成對自定義控件賦值;

2. 需要加入[assembly: Fiddler.RequiredVersion("x.x.x.x")]特性,位置不限。Fiddler目錄有基於.Net Framework 2.0和4.0的版本,本例使用4.0,CLR版本兼容性、X64兼容性等具體內容請自行翻閱文檔。

 AddToTab方法大致內容如下:

復制代碼
public override void AddToTab(TabPage tabPage)
{
    utrlJson = new UserControl_JsonView() { Dock = DockStyle.Fill };
    tabPage.Text = "MyJson";
    tabPage.Controls.Add(utrlJson);
}
復制代碼

這里使用了一個UserControl,暴露一個Content屬性,內部使用TextBox和TreeView展示Json文本與JsonNode結構。

取消Fiddler引用及排除Plugin.cs,將項目類型設置為Windows應用程序,可以得到Form窗體程序;加入引用及Plugin.cs,將項目類型設置成類庫,扔到%Program Files%/Fiddler/Inspectors目錄便是Fiddler插件。這里加入了文本定位、節點分層展開、節點值復制(可以在TreeView上使用Ctrl+C進行智能復制)等方法,方便使用。

PART3:后記

感覺代碼還是比較亂,JsonNode創建方法還可以提煉;水平所限,TextBox光標定位正則不夠強大,需要優化;功能上講,可以加入設置項以組織編碼格式、Header解析、控件視圖等。代碼文件及二進制文件(戳這里)奉上。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM