如何在LinqPad中使用Dump()方法输出Newtonsoft JObject?

26
在LinqPad上,尝试在Newtonsoft JSON.Net的JObject对象上调用`.Dump()`方法会出现异常:
运行时绑定异常:'Newtonsoft.Json.Linq.JObject'不包含'Dump'的定义。
这对于LinqPad中几乎所有其他内容都起作用。我想找出一种方法,在输出属性名称、值等的同时,将Newtonsoft JObject对象Dump出来,就像其他对象一样。
我已经找到了如何将其转储为JSON字符串,但我希望看到输出一个对象而不仅仅是一个文本字符串。
9个回答

30

对于任何想要从JSON字符串获取漂亮的LINQPad输出的人,将其反序列化为ExpandoObject是一种有效的方法,并可以递归地处理数据中可能存在的任何层级结构:

JsonConvert.DeserializeObject<ExpandoObject>(myJSONString).Dump();

将其扩展到覆盖实际问题,针对JObject的扩展方法如下所示:

public static class ExtMethods
{
    public static JObject DumpPretty(this JObject jo)
    {
        var jsonString = JsonConvert.SerializeObject(jo);
        JsonConvert.DeserializeObject<ExpandoObject>(jsonString).Dump();

        return jo;  // return input in the spirit of LINQPad's Dump() method.
    }
}

虽然不是最高效的方法,但在使用LINQPad进行快速操作时它会发挥作用。


1
很棒的解决方案!到目前为止,这是与linqpads dump最兼容的答案。 - Jake Almer
有关其他JSON类型的扩展,请参见https://dev59.com/W2Yq5IYBdhLWcg3whRHV#35042280。 - m3tikn0b
9
你可以使用jo.ToObject<ExpandoObject>()来代替序列化/反序列化。 - Jeff Mercado
如何使用函数名Dump,而不是DumpPretty。我可以使用jo.Dump()!! - s.c

22

这是一个静态扩展方法,因此您可以将其视为静态方法来调用:

LINQPad.Extensions.Dump(jObject);

我发现在某些情况下(我猜测)编译器无法绑定到扩展名,导致出现这种情况。

LinqPad的网站上有一篇文章一篇博客文章介绍如何在dynamic对象中使用Dump()

你可以尝试创建另一个Dump()扩展,检查JObject的属性,并创建一个可以漂亮地Dump的字典。

类似于这样的东西:(基于JObject定义的完整的WAG):

var values = jObject.Properties.ToDictionary(p=>p.Name, p=>p.Value);
values.Dump();

当然,您可以为嵌套对象等添加递归:

//Usage: GetProperties(jObject).Dump();
public static object GetProperties(object o)
{
    JObject j = o as JObject;
    if(j == null)
    {
        return o.ToString();
    }
    return j.Properties().ToDictionary(p=>p.Name,p=>GetProperties(p.Value));
}

1
不幸的是,这并没有真正解决问题。它会输出一组嵌套的值,但不显示属性名称,因此您无法理解它。 - Ken Mason
2
这篇博客文章是关于 ExpandoObject 以及它实现的 IDictionary<string, object> 的特定内容,我认为。虽然 JObject 实现了 IDictionary<string, JToken>,可能会产生良好的输出结果,但它并不是 通用 的动态对象处理方式。 - Jon Skeet
@KenMason 那么你就不能使用 LinqPad 的 Dump(),因为它的工作原理就是这样。 - D Stanley
不,对于Newtonsoft的JObject对象,它不是这样工作的。这就是这篇文章的重点。 :) - Ken Mason
我的观点是LinqPad的Dump()函数工作原理(我推测)是通过反射获取公共属性,并对集合、字典等进行特殊处理。 - D Stanley
Dump()没有特殊处理来查看JObject的弱类型Properties集合以了解如何漂亮地打印它。 - D Stanley

8
我的猜测是您正在做这样的事情:
dynamic foo = ...;
foo.Dump();

扩展方法(Dump就是其中之一)不适用于动态类型。如果您使用以下代码:

object foo = ...;
foo.Dump();

那么我希望它能够“正常”工作。但是,因为JObject上的属性不是JSON属性,而是动态提供的,所以它可能并不会做你实际想要的事情。

(按照D Stanley的答案显式调用扩展方法也可以,但您可能会发现将其作为扩展方法仍然更方便。)

编辑:我强烈怀疑Dump根本无法给您想要的内容,因为它对Json.NET一无所知,并且可能无法处理动态对象,就像调试器一样。您最好编写自己的Dump方法来迭代JObject的属性并递归地转储它们。如果您仍然希望找到开箱即用的内容,则应查看Json.NET文档而不是寻找LINQPad,尽管我不知道您是否会找到任何内容。


不幸的是,这并没有真正解决问题。它输出了一组嵌套的值,但没有显示属性名称,因此您无法理解它。 - Ken Mason
3
这段话的意思是:@KenMason在调用LINQPad中的“Dump”方法。我在回答中说,它可能不会做你想要的事情...因为它使用了实际对象的属性,而不是动态属性。我怀疑你可能找不到完全按照你的要求进行操作的方法——至少不是开箱即用的。在我的回答和D Stanley的回答中,两者都是合理的(依我所见),就你如何使用“Dump”而言...只是“Dump”不能做你想要的事情。 - Jon Skeet

3

更新

LINQPad的最新版本会自动对JSON.NET对象(如JObject)进行漂亮的格式化,类似于被接受的答案中的ExpandoObject

LINQPad截图

原始答案

有一个专门为此而编写的可视化工具

添加 NMyVision.LinqPad.JsonVisualizer NuGet包并调用JsonVisualizer.DumpJson(jObject),将会出现一个带有漂亮树形结构的选项卡。

JsonVisualizer输出


2

在扩展rdavisau的想法后,我得出了这个结果:

public static class ExtMethods
{
    public static object Dumpable(this JToken t)
    {
        if(t is JObject)
        {
            var json = JsonConvert.SerializeObject(t);
            return JsonConvert.DeserializeObject<ExpandoObject>(json);
        }
        else if(t is JArray)
        {
            return (t as JArray).Select(Dumpable);
        }
        else if(t is JValue)
        {
            return t.ToString();
        }
        else if(t is JProperty)
        {
            var p = (t as JProperty);
            return new { Name=p.Name, Value=Dumpable(p.Value) };
        }
        else
        {
            throw new Exception("unexpected type: " + t.GetType().ToString());
        }
    }

    public static JToken DumpPretty(this JToken t)
    {
        t.Dumpable().Dump();
        return t;
    }
}

public static object Dumpable(JToken t)
{
    return t.Dumpable();
}

这样,您还可以漂亮地打印数组和查询结果,它们不是JObjects。


2
这个方法似乎很不错:
dynamic dyn = ... // parse some JSON or whatever
((JObject)dyn).Properties().ToDictionary(p=>p.Name, p=>p.Value).Dump();

如果你有一个列表,这个方法可以使用:
dynamic[] dyns = ...
dyns.Cast<JObject>().Select(o => o.Properties().ToDictionary(p => p.Name, p => p.Value)).Dump();

此外,如果您有一个linqpad程序,这也很方便:
static class JsonNetDumper {
  public static IEnumerable<IDictionary<string, object>> ToDumpable(this IEnumerable<object> rg) { 
    return rg.Cast<JObject>().Select(o => o.Properties().ToDictionary(p => p.Name, p => (object)p.Value));
  }
}

你可以像这样使用:

dynamic[] dyns = ...
dyns.ToDumpable().Dump();

1
使用LINQPad的新功能ToDump,您可以直接.Dump()一个Newtonsoft JSON.Net JObject。将此片段添加到LINQPad的My Extensions查询中。
static Object ToDump(Object input)
{
    var json = input as JObject;
    if (json != null)
        return json.ToObject<ExpandoObject>();
    return input;
}

你还需要添加对Netwonsoft.Json的引用,就像你为主查询所做的那样。

1

JObject 实际上没有属性,它是一堆 JArrayJProperty 的混杂物。您可以按照 先前的答案 提示直接使用扩展方法,或将动态转换为 object 并转储。但最好将其转回其 JSON 表示形式,并使用 .ToString() 转储。

var data = JsonConvert.DeserializeObject(@"{...}");
LINQPad.Extensions.Dump(data); // gives JObject, JArray, etc
((object)data).Dump(); // gives JObject, JArray, etc
((string)data.ToString()).Dump(); // given the JSON string back again

关于这个话题,我发现了一个.DumpJson方法,通过将普通对象进行转换成 json 格式。主要提及它是因为它描述了如何编写自定义的Dump扩展。


-1
今天我刚试过使用 JObject,并通过 Nuget 添加了 JSON.NET 库。当 data 是一个 JObject 时,当我使用 data.Dump() 时,我得到了结构化数据(而且没有错误)。

enter image description here


这种方法的问题在于你只能看到值,而无法看到键。 - Arturo Torres Sánchez
真实 - 符合我的目的,但不是解决方案。 - Jeremy Noble

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接