如何使用 json.net 构建用于序列化的对象层次结构?

Posted

技术标签:

【中文标题】如何使用 json.net 构建用于序列化的对象层次结构?【英文标题】:How to build object hierarchy for serialization with json.net? 【发布时间】:2011-08-24 03:33:38 【问题描述】:

我正在尝试正确编写代码来构建数据结构以序列化为 json。

我正在使用 json.net。

我不想创建一堆类来保存这些数据,因为我认为应该有一些类已经在 json.net 中这样做了

我已经在一系列嵌套循环中获得了我需要的所有数据,现在我只想在运行 JsonConvert.SerializeObject 之前将它们添加到对象层次结构中。

我已经尝试过这样的代码,但它似乎不起作用

JArray container = new JArray();

        container.Add(new JObject(new JProperty("name", "Client1"), new JProperty("projects", new JArray())));

        container[0].AddAfterSelf(new JObject(new JProperty("projects", new JArray())));            
        container[1].AddAfterSelf(new JObject(new JProperty("projects", "Project2")));
        container[1].AddAfterSelf(new JObject(new JProperty("projects", "Project3")));
        container.Add(new JProperty("name", "Client2"));            

        var test = JsonConvert.SerializeObject(container);

问题是当我使用 [i] 时。或 ElementAt(i) 访问结构中的某处,要么 .Add() 丢失,要么 .ElementAt 不存在。 如何逐步完成数据结构以使其很好地输出以下内容,或者我是否必须为所有这些创建自己的容器类?

这是我正在尝试制作的数据格式。

[
    
    "name": "student1",
    "projects": 
    [
        
        "name": "Project1",
        "tasks": 
                [
                    
                    "name": "task1",
                    "id": 2
                    
                ],
        "id": 6
        
    ]
,
    
    "name": "Student2",
    "projects": [
                
                "name": "Project1",
                "tasks": [
                         
                         "name": "Task2",
                         "id": 1
                         ,
                         
                         "name": "Task3",
                         "id": 3
                         ,
                         
                         "name": "Task4",
                         "id": 4
                         
                         ],
                "id": 2

等等……

【问题讨论】:

【参考方案1】:

我认为您要问的是如何在 json 中序列化复杂的业务对象,但只公开某些属性。

换句话说,您已经有一个学生列表,但您只想通过 json 发送非常具体的数据。如果我错了,这个答案将不适合您的需求。

因此,假设您有一个学生列表,其项目属性具有任务的内部属性,这就是我无需创建大量新课程的方式,我使用匿名对象。

创建匿名对象列表后,我只需将它们转换为 json 字符串。

正如cmets中指出的你不需要使用json.net,这个功能在框架中是可用的,然后添加对System.Web.Extensions.dll的引用

使用 System.Web.Script.Serialization;

var jsonStudents = new List<object>();

foreach (var student in students)

    jsonStudents.Add(new
    
        student.Id,         //anonymous properties automatically pick up the name of the property you pass them, this will be called Id
        FullName = student.FirstName + " " + student.LastName, //if you want to name a property yourself use this notation
        Projects = student.Projects.Select(p => new //this will be an enumerable of nested anonymous objects, we're partially selecting project properties
        
            p.Id,
            p.Name,
            Tasks = p.Tasks.Select(t => new //nesting another level
            
                t.Id,
                t.Name
            )
        )
    );


var serializer = new javascriptSerializer();

var jsonString = serializer.Serialize(jsonStudents);

如果你真的想使用循环,你可以这样做让你在创建项目和任务时做更复杂的事情:

var jsonStudents = new List<object>();

foreach (var student in students)

    var tempStudent = new
    
        student.Id,         //anonymous properties automatically pick up the name of the property you pass them, this will be called Id
        FullName = student.FirstName + " " + student.LastName, //if you want to name a property yourself use this notation
        Projects = new List<object>()
    ;

    foreach (var project in student.Projects)
    
        var tempProject = new 
            project.Id,
            project.Name,
            Tasks = new List<object>()
        ;

        foreach (var task in project.Tasks)
        
            tempProject.Tasks.Add(new 
                task.Id,
                task.Name
            );
        

        tempStudent.Projects.Add(tempProject);
    

    jsonStudents.Add(tempStudent);


var serializer = new JavaScriptSerializer();

var jsonString = serializer.Serialize(jsonStudents);

【讨论】:

嗯,这看起来很聪明,似乎取决于 linq2sql 关系。一个整洁的解决方案。你知道这是否有助于提高性能,因为编译器应该知道子对象需要哪些属性,因此可以巧妙地处理查询(更多的是 linq2sql 问题)我计划在它工作后优化我的数据访问. 我已经将它与 POCO、Linq2Sql 和 LinqToEntities 一起使用,您可以使用任何 .Net 对象来执行此操作。我的回答中实际上没有 Linq2Sql,我不完全确定您的意思,尽管我认为您只是指 Linq 时使用的是 Linq2Sql。性能方面,匿名类实际上是在 IL afaik 中声明的,因此它实际上只是为您节省了几行代码,如果您使用您所描述的模型来执行此操作,那么剩下的几乎就是您必须做的事情. IMO 这是最好的答案......你甚至不需要 json.net,只需使用内置的 json 序列化程序 之所以提到Linq2Sql是因为student.Projects调用,student怎么知道可以引用Projects? 我猜这实际上是匿名的。但是这个答案对我的一系列嵌套循环有帮助吗?如果我想在语句中间获取更多数据怎么办?【参考方案2】:

这是根据您的问题生成准确输出的代码(需要using Newtonsoft.Json.Linq;):

var json = new JArray(
                new JObject(
                    new JProperty("name", "student1"),
                    new JProperty("projects", 
                        new JArray(
                            new JObject(
                                new JProperty("name", "Project1"),
                                new JProperty("tasks", 
                                    new JArray(
                                        new JObject(
                                            new JProperty("name", "task1"),
                                            new JProperty("id", 2)
                                            )
                                        )
                                    ),
                                new JProperty("id", 6)
                            )
                        )
                    )
                ),
                new JObject(
                    new JProperty("name", "student2"),
                    new JProperty("projects", 
                        new JArray(
                            new JObject(
                                new JProperty("name", "Project1"),
                                new JProperty("tasks", 
                                    new JArray(
                                        new JObject(
                                            new JProperty("name", "task2"),
                                            new JProperty("id", 1)
                                            ),
                                        new JObject(
                                            new JProperty("name", "task3"),
                                            new JProperty("id", 3)
                                            ),
                                        new JObject(
                                            new JProperty("name", "task4"),
                                            new JProperty("id", 4)
                                            )
                                        )
                                    ),
                                new JProperty("id", 2)
                            )
                        )
                    )
                )
            );
var jsonString = json.ToString();

我相信使用 Json.Net Linq 语法具有很大的优势,即可以对生成的 C# 代码进行格式化,使其几乎具有与您尝试生成的 JSON 相同的结构。

更新

如果您想在构建 Json 对象后对其进行操作,请查看此示例,该示例仅使用一个 student 构建外部数组,然后附加另一个:

// create an isolated Student instance:
var student2 = new JObject(
                    new JProperty("name", "student2"),
                    new JProperty("projects", 
                        new JArray(
                            new JObject(
                                new JProperty("name", "Project1"),
                                new JProperty("tasks", 
                                    new JArray(
                                        new JObject(
                                            new JProperty("name", "task2"),
                                            new JProperty("id", 1)
                                            ),
                                        new JObject(
                                            new JProperty("name", "task3"),
                                            new JProperty("id", 3)
                                            ),
                                        new JObject(
                                            new JProperty("name", "task4"),
                                            new JProperty("id", 4)
                                            )
                                        )
                                    ),
                                new JProperty("id", 2)
                            )
                        )
                    )
                );

var json = new JArray(
                new JObject(
                    new JProperty("name", "student1"),
                    new JProperty("projects", 
                        new JArray(
                            new JObject(
                                new JProperty("name", "Project1"),
                                new JProperty("tasks", 
                                    new JArray(
                                        new JObject(
                                            new JProperty("name", "task1"),
                                            new JProperty("id", 2)
                                            )
                                        )
                                    ),
                                new JProperty("id", 6)
                            )
                        )
                    )
                )
            );

// now, add the student2 instance to the array:
json                             // which is an JArray
    .Last                        // gets the last Array item, i.e. "student1"
    .AddAfterSelf(student2);     // adds this which hence becomes the new last one

这个想法是您可以以相同的方式将相同的原理应用于结构的任何其他部分。

HTH...

【讨论】:

您好感谢您的回答,但我想在一系列循环中构建 json,即不是同时进行?如果我必须这样做,我想我必须一步构建它,对吗?或者我可以在构建后修改数据结构吗? 不,你不必一步构建它,你也可以稍后修改结构。请参阅上面的更新答案。 好的,这更接近我的需要,但是如果我想在树的下方添加元素怎么办?如果我想添加而不是 AddAfterSelf,该怎么办?我有一系列运行数据访问的嵌套循环(我知道这可能对性能不利,但我现在不打算解决这个问题)。我希望这是有道理的。当我尝试做 container.Last.Last.AddAfterSelf(new JObject(new JProperty("test", "testItem")));我收到错误无法将 Newtonsoft.Json.Linq.JObject 添加到 Newtonsoft.Json.Linq.JObject。你明白我在做什么吗? 这个错误是因为container.Last.Last返回了“projects”property,所以你只能在这之后再添加一个JProperty,而不是JObject。如果您确实想将新的JObject 添加到“项目”数组,请执行以下操作:container.Last.SelectToken("projects").Last.AddAfterSelf(new JObject(...))。这会从 container 中的最后一项获取“projects”属性,并将新的JObject 添加到“projects”数组中。因此,结合使用 FirstLast 属性以及 SelectToken()AddAfterSelf() 方法,您应该能够实现您所追求的目标。 看起来如此接近!现在 Array 似乎不喜欢这样,因为它是空的,所以 .Last 无法选择要添加的任何内容?如果你能告诉我如何以另一种方式做到这一点,我可以做到这一点,非常感谢你的帮助!【参考方案3】:

最后我使用了这些模型。

public class JStudent

    public List<JProject> projects = new List<JProject>();
    public string name;
    public string id;

public class JProject

    public List<JTask> tasks = new List<JTask>();
    public string name;
    public string id;

public class JTask

    public string name;
    public string id;


它现在运行良好。有没有更好的方法来做到这一点?

【讨论】:

如果我是你,我会像你在这里所做的那样简单地构建你的对象层次结构,并将所有序列化留给 json.net。如果您在序列化中有特定需求,您可以通过属性来控制:james.newtonking.com/projects/json/help 为什么这个答案没有被评为更高?这绝对是可怕的。谢谢!

以上是关于如何使用 json.net 构建用于序列化的对象层次结构?的主要内容,如果未能解决你的问题,请参考以下文章

自定义 JSON.Net 输出

JSON.net:如何在不使用默认构造函数的情况下反序列化?

如何使用 Json.NET 将 XML 序列化为 JSON 对象

Json.NET 是不是缓存类型的序列化信息?

如何使用json.net自定义反序列化为对象

JSON.NET与LINQ序列化示例教程