I'm trying to properly write code to build a data structure to serialize into json.
I'm using json.net.
I don't want to create a bunch of classes to hold this data, as I thought there should be some classes that will already do this in json.net
I've already got all the data I need in a series of nested loops, and now I just want to add them to an object hierarchy before I run JsonConvert.SerializeObject on it.
I've already tried code like this, but it doesn't seem to work
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);
The problem is that when I use [i]. or ElementAt(i) to access somewhere in the structure, either .Add() is missing or .ElementAt isn't there. How do I step through the data structure to make this nicely output the below, or do I have to create my own container class for all of this?
This is the data format I am trying to make.
[
{
"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
etc...
I think what you're asking is how to serialize complex business objects in json, but only expose certain properties.
In other words you already have a list of students, but you only want to send very specific data via json. If I'm wrong this answer won't suit your needs.
So assuming you have a list of students, with a projects property that has an inner property of tasks, this is how I do it without having to create loads of new classes, I use anonymous objects.
Once I've created my list of anonymous objects, I simply turn them into a json string.
As pointed out in the comments you don't need to use json.net, this functionality is available in the framework, add a reference to System.Web.Extensions.dll
then
using 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);
If you really want to use loops you can do this to allow you to do more complicated things in the creating of the projects and tasks:
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);
this is the code which generates the exact output from your question (requires a 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();
I believe using the Json.Net Linq syntax has the great advantage that the resulting C# code can be formatted so that it almost has the same structure as the JSON you're trying to generate.
UPDATE
If you want to manipulate the Json object once it has been built, look at this example which builds the outer array with only one student, and then appends another one:
// 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
The idea is that you can apply the very same principle to any other portion of the structure in the same way.
HTH...
In the end I have used these Models.
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;
}
It is now working perfectly. Is there any better way to do this?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With