I am webscraping some data and trying to write the scraped data to a json file using c# newtonsoft.Json.
I get stuck when writing the data to the Json file in my controller.
The multidimensional arrays in c# confuse me.
Thanks in advance.
This is an example of the Json file I am trying to create:
[
{
"testmodule1": {
"name": {
"required": true,
"options": [
"option1",
"option2"
]
},
"admin": {
"required": true,
"options": [
"option1",
"option2"
]
},
"path": {
"required": true,
"options": [
"option1",
"option2"
]
}
}
},
{
"testmodule2": {
"name": {
"required": true,
"options": [
"option1",
"option2"
]
},
"server": {
"required": false,
"options": [
]
},
"port": {
"required": true,
"options": [
"option1",
"option2"
]
}
}
}
]
These are my classes:
public class JsonData
{
public Dictionary<string, JsonParameters> modulename { get; set; }
}
public class JsonParameters
{
public JsonParametersData parameter { get; set; }
}
public class JsonParametersData
{
public bool required { get; set; }
public List<string> options { get; set; }
}
This is my controller, here is where I get stuck. the name modulename does not exist in the current context:
public class WebscrapeController : Controller
{
// GET: Webscrape
public ActionResult Index()
{
List<JsonData> data = new List<JsonData>();
data.Add(new JsonData()
{
modulename = new Dictionary<string, JsonParameters>()
{
modulename.Add("testmodule1", new JsonParameters()
{
parameter = new JsonParametersData()
{
required = true,
options = new List<string>()
{
"option1",
"option2"
}
}
})
}
});
string json = JsonConvert.SerializeObject(data.ToArray());
//write string to file
System.IO.File.WriteAllText(
#"C:mypath",
json);
}
}
Note that the property names "testmodule1" and "testmodule2" as well as "name", "admin", "path", "server" are arbitrary; they differ for each array.
Since the property names "testmodule1" and "testmodule2" as well as "name", "admin", "path", "server" and "port" are arbitrary and not known in advance, you need to model your results array as a List<Dictionary<string, Dictionary<string, JsonParametersData>>>. That is because, when serializing a dictionary to JSON using Json.NET, the dictionary keys become JSON property names.
Thus the JSON above can be created as follows:
// Allocate results using collection initializer syntax for the lists and for the dictionaries.
// https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/object-and-collection-initializers#collection-initializers
// https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/how-to-initialize-a-dictionary-with-a-collection-initializer
var results = new List<Dictionary<string, Dictionary<string, JsonParametersData>>>()
{
new Dictionary<string, Dictionary<string, JsonParametersData>>()
{
{
"testmodule1",
new Dictionary<string, JsonParametersData>()
{
{
"name",
new JsonParametersData
{
required = true,
options = new List<string>() { "option1", "option2" },
}
},
{
"admin",
new JsonParametersData
{
required = true,
options = new List<string>() { "option1", "option2" },
}
},
{
"path",
new JsonParametersData
{
required = true,
options = new List<string>() { "option1", "option2" },
}
}
}
},
}
};
var moduleName = "testmodule2";
var moduleParameters = new [] { "name", "server", "port" };
// Now add another result, allocating the dictionary with collection initializer syntax also
results.Add(new Dictionary<string, Dictionary<string, JsonParametersData>>()
{
{
moduleName,
// Loop through the parameters and convert them to a dictionary,
// where the parameter name is the key and the corresponding JsonParametersData is the value
moduleParameters
.ToDictionary(n => n,
n => new JsonParametersData
{
required = true,
options = new List<string>() { "option1", "option2" },
})
}
});
var json = JsonConvert.SerializeObject(results, Formatting.Indented);
Notes:
For documentation on how dictionaries are serialized to JSON, see Serialize a Dictionary and Serialization Guide: Dictionaries and Hashtables.
I am initializing the outermost List<T> using collection initializer syntax.
I am also initializing the dictionaries using collection initializer syntax as shown in How to: Initialize a Dictionary with a Collection Initializer (C# Programming Guide).
Given a collection of parameter names and a way to get the JsonParametersData for each one (not shown in the question), the LINQ extension method Enumerable.ToDictionary<TSource, TKey, TElement>() can be used to construct a Dictionary<string, JsonParametersData> from the parameter collection.
Working sample .Net fiddle here.
Here is a different approach using Newtonsoft JObject.
https://dotnetfiddle.net/TdFDQc
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
namespace StackOverflow
{
public class Program
{
public static void Main(string[] args)
{
JArray array = new JArray();
// Module 1
JObject parameter = new JObject();
AddParameter(parameter, "name", true, new[] { "option1", "option2" });
AddParameter(parameter, "admin", true, new[] { "option1", "option2" });
AddParameter(parameter, "path", false, new[] { "option1", "option2", "option3" });
JObject module = new JObject();
module.Add("testmodule1", parameter);
array.Add(module);
// Module 2
parameter = new JObject();
AddParameter(parameter, "name", true, new[] { "option1", "option2" });
AddParameter(parameter, "server", false, Array.Empty<string>());
AddParameter(parameter, "port", true, new[] { "option1", "option2", "option3" });
module = new JObject();
module.Add("testmodule2", parameter);
array.Add(module);
// Display result
var json = array.ToString();
Console.WriteLine(json);
}
static void AddParameter(JObject jObject, string name, bool required, string[] options)
{
JObject parameterProperties = new JObject();
parameterProperties.Add("required", JToken.FromObject(required));
parameterProperties.Add("options", JToken.FromObject(options));
jObject.Add(name, parameterProperties);
}
}
}
Related
I have a json object that I need to post to a gateway. It looks as follows.
{
"client_id": "test",
"non_personalized_ads": false,
"events": [
{
"name": "test",
"params": {
"items": [
{
"itemitem1": "itemitem1value",
"itemitem1number": 1
}
],
"stringparm1": "stringValue1",
"intparm1": 1
}
}
]
}
I am trying to create this as a object class in my C# project. The issue is that both the names of these parms are configurable. So none of these are static property names.
"itemitem1": "itemitem1value",
"itemitem1number": 1
"stringparm1": "stringValue1",
"intparm1": 1
So in an attempt to achieve that I went with a Dictionary<string, object> as the value can be a number or a string I went with an Object.
public class EventParameter
{
public Dictionary<string, object> Parameters { get; set; }
public Dictionary<string, object> Items { get; set; }
public EventParameter()
{
Parameters = new Dictionary<string, object>();
Items = new Dictionary<string, object>();
}
public void AddEventParameter(string name, object value)
{
Parameters.Add(name, value);
}
}
unfortunately when I do a json pars on this I get. Which is incorrect as my Parameters should be under parms and not under their own object.
{
"client_id": "test",
"non_personalized_ads": false,
"events": [
{
"name": "hit_event",
"params": {
"Parameters": {
"TestString": "test",
"testInt": 1
},
"Items": {
"test": 1,
"test2": 1,
"test3": "test"
}
}
}
]
}
So how do i get Parameters moved up to parms? It feels like parms needs to be a Dictionary<string, object> but then its going to be missing its items list
"Parameters": {
"TestString": "test",
"testInt": 1
},
Im assuming there's something missing in my model design. I have been looking at this too long i cant come up with the solution. My other idea was could this be done by adding a custom json sterilizer?
Let me know if you want my full model design or the unit test I am using to test with.
unit test
[Fact]
public void Test1()
{
var postData = new Event() { Name = "EventName" };
postData.EventParameters.AddEventParameter("TestString", "test");
postData.EventParameters.AddEventParameter("testInt", 1);
postData.EventParameters.Items = new Dictionary<string, object>()
{
{ "test", 1 },
{ "test2", 1 },
{ "test3", "test" },
};
var data = new EventData()
{
ClientId = "test",
Events = new []
{
postData
}
};
var options = new JsonSerializerOptions { IgnoreNullValues = true };
var hold =JsonSerializer.Serialize(data);
// add breakpoint here
int i = 1;
}
Here's a class that serialises to the first json you showed:
class X
{
public string client_id {get; set;}
public bool non_personalized_ads {get; set;}
public List<EventParameter> events { get;set;}
}
class EventParameter {
public string name {get; set;}
public Dictionary<string, object> #params {get; set;}
}
Test code:
var x = new X
{
client_id = "test",
non_personalized_ads = false,
events = new List<EventParameter>{
new EventParameter
{
name = "test1",
#params = new Dictionary<string, object>{
{ "items", new Dictionary<string, object> {
{"itemitem1", "itemitem1value"},
{"itemitem1number", 1}
}},
{ "stringparm1", "stringValue1"},
{"intparm1", 1}
}
}
}
};
var json = System.Text.Json.JsonSerializer.Serialize(x, options: new System.Text.Json.JsonSerializerOptions{WriteIndented = true});
Console.WriteLine(json);
Is it possible, using Newtonsoft Json.NET, to use one of the properties of a class that is being serialised (via a collection) as the key for that class?
For example, take the following classes in C#
public class MyClass
{
public string UniqueId { get; set; }
public string Value { get; set; }
}
public class MyCollection : List<MyClass>
{ }
If I run the following to serialise the collection...
var col = new MyCollection() {
new MyClass { UniqueId = "First", Value = "This") },
new MyClass { UniqueId = "Second", Value = "That") }
};
string json = JsonConvert.SerializeObject(col);
This will result in the following JSON, with an array containing each MyClass.
[
{
"UniqueId": "First",
"Value": "This"
},
{
"UniqueId": "Second",
"Value": "That"
}
]
Is there any way to force Newtonsoft to create the following instead, where an object (rather than array) is created with each class referenced by the UniqueId property as the key?
{
"First":
{
"Value": "This"
},
"Second":
{
"Value": "That"
}
}
Or if easier...
{
"First":
{
"UniqueId": "First",
"Value": "This"
},
"Second":
{
"UniqueId": "Second",
"Value": "That"
}
}
I'm aware that using a Dictionary<> will result in what I want, by using the same value from the property as the key... however, this is unordered and I need the List<> so it's in order.
An OrderedDictionary can be used to keep the order of the items added, or a SortedDictionary in order to sort the items by key.
Given the following model:
public class MyClass
{
public string UniqueId { get; set; }
public string Value { get; set; }
}
And the following instances:
var first = new MyClass {UniqueId = "First", Value = "This"};
var second = new MyClass {UniqueId = "Second", Value = "That"};
var third = new MyClass {UniqueId = "Third", Value = "Foo"};
var fourth = new MyClass {UniqueId = "Fourth", Value = "Bar"};
Using an OrderedDictionary
var dictionary = new OrderedDictionary()
{
{ first.UniqueId, first },
{ second.UniqueId, second },
{ third.UniqueId, first },
{ fourth.UniqueId, first },
};
string json = JsonConvert.SerializeObject(dictionary, Formatting.Indented);
This will keep the order of the added items. Output json is the following:
{
"First": {
"UniqueId": "First",
"Value": "This"
},
"Second": {
"UniqueId": "Second",
"Value": "That"
},
"Third": {
"UniqueId": "First",
"Value": "This"
},
"Fourth": {
"UniqueId": "First",
"Value": "This"
}
}
Using a SortedDictionary
var first = new MyClass {UniqueId = "First", Value = "This"};
var second = new MyClass {UniqueId = "Second", Value = "That"};
var third = new MyClass {UniqueId = "Third", Value = "Foo"};
var fourth = new MyClass {UniqueId = "Fourth", Value = "Bar"};
var dictionary = new SortedDictionary<string, MyClass>
{
{ first.UniqueId, first },
{ second.UniqueId, second },
{ third.UniqueId, first },
{ fourth.UniqueId, first },
};
string json = JsonConvert.SerializeObject(dictionary, Formatting.Indented);
This will sort the items by key (Fourth is the 2nd item, not the 4th). Output json is the following:
{
"First": {
"UniqueId": "First",
"Value": "This"
},
"Fourth": {
"UniqueId": "First",
"Value": "This"
},
"Second": {
"UniqueId": "Second",
"Value": "That"
},
"Third": {
"UniqueId": "First",
"Value": "This"
}
}
A strongly-typed alternative to OrderedDictionary - use a List<KeyValuePair<key, value>> with a Dictionary.
So, given the following model:
public class MyClass
{
public string UniqueId { get; set; }
public string Value { get; set; }
}
And the following instances:
var first = new MyClass {UniqueId = "First", Value = "This"};
var second = new MyClass {UniqueId = "Second", Value = "That"};
var third = new MyClass {UniqueId = "Third", Value = "Foo"};
var fourth = new MyClass {UniqueId = "Fourth", Value = "Bar"};
Instead of creating and initializing the Dictionary like this:
var dictionary = new Dictionary<string, MyClass>
{
{ first.UniqueId, first },
{ second.UniqueId, second },
{ third.UniqueId, first },
{ fourth.UniqueId, first },
};
We can create and initialize a KeyValuePair list and then create the Dictionary:
var pairs = new List<KeyValuePair<string, MyClass>>
{
new KeyValuePair<string, MyClass>(first.UniqueId, first),
new KeyValuePair<string, MyClass>(second.UniqueId, second),
new KeyValuePair<string, MyClass>(third.UniqueId, third),
new KeyValuePair<string, MyClass>(fourth.UniqueId, fourth),
};
var dictionary = new Dictionary<string, MyClass>(pairs);
This will keep the order of the items added to the KeyValuePair list. Serializing the Dictionary:
string json = JsonConvert.SerializeObject(dictionary, Formatting.Indented);
Output json is the following:
{
"First": {
"UniqueId": "First",
"Value": "This"
},
"Second": {
"UniqueId": "Second",
"Value": "That"
},
"Third": {
"UniqueId": "Third",
"Value": "Foo"
},
"Fourth": {
"UniqueId": "Fourth",
"Value": "Bar"
}
}
Suppose, I have a object that has a sub property that references itself.
public class Person
{
public Person Parent { get; set; }
public string Name { get; set; }
public ObservableCollection<Person> Children { get; set; } = new ObservableCollection<Person>();
}
When I try to return it,
var pp = new Person();
pp.Name = "TONY";
pp.Children.Add(new Person()
{
Parent = pp,
Name = "ESTHER"
});
return new JsonResult(pp);
it comes truncated, some Exception happens while serializing it.
{"parent":null,"name":"TONY","children":[{
SyntaxError: JSON.parse: end of data while reading object contents at line 1 column 43 of the JSON data
On ASP Net we could do this:
config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling
= Newtonsoft.Json.ReferenceLoopHandling.Serialize;
config.Formatters.JsonFormatter.SerializerSettings.PreserveReferencesHandling
= Newtonsoft.Json.PreserveReferencesHandling.Objects;
How can I configure Azure Function Json Serializer, to allow it?
Looking at the overloaded methods, I found:
then I used it with this code:
var serializerSettings = new Newtonsoft.Json.JsonSerializerSettings()
{
ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Serialize,
PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.All
};
return new JsonResult(p, serializerSettings);
And it worked.
{
"$id": "1",
"Parent": null,
"Name": "TONY",
"Children": {
"$id": "2",
"$values": [
{
"$id": "3",
"Parent": {
"$ref": "1"
},
"Name": "ESTHER",
"Children": {
"$id": "4",
"$values": []
}
}
]
}
}
You can also get to config using:
var config = req.GetConfiguration();
config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Serialize;
config.Formatters.JsonFormatter.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;
The only downside is that you need to create on each function but you could add a custom attribute to make that easier.
How do I create a JSON array using Newtonsoft.JSON (json.net) from this json string
[
{
"Cells": {
"results": [
{
"Key": "Title",
"Value": "hello",
"ValueType": "Edm.String"
},
{
"Key": "Size",
"Value": "54549",
"ValueType": "Edm.Int64"
},
{
"Key": "Path",
"Value": "http://somesite/a/hello.pptx",
"ValueType": "Edm.String"
},
{
"Key": "Summary",
"Value": "Some summary <ddd/> interesting reading <ddd/> nice book <ddd/> ",
"ValueType": "Edm.String"
},
{
"Key": "Name",
"Value": "http://somesite",
"ValueType": "Edm.String"
}
]
}
},
{
"Cells": {
"results": [
{
"Key": "Title",
"Value": "hi joe",
"ValueType": "Edm.String"
},
{
"Key": "Size",
"Value": "41234",
"ValueType": "Edm.Int64"
},
{
"Key": "Path",
"Value": "http://someothersite/interesting/hi.pptx",
"ValueType": "Edm.String"
},
{
"Key": "Summary",
"Value": "Some summary <ddd/> interesting reading <ddd/> nice book <ddd/> ",
"ValueType": "Edm.String"
},
{
"Key": "Name",
"Value": "http://somesite",
"ValueType": "Edm.String"
}
]
}
}
]
json2csharp gives me following classes for this structure
public class Result
{
public string Key { get; set; }
public string Value { get; set; }
public string ValueType { get; set; }
}
public class Cells
{
public List<Result> results { get; set; }
}
public class RootObject
{
public Cells Cells { get; set; }
}
How do I use these classes to create json array?
UPDATE AND SOLUTION
this will work
static void Main(string[] args)
{
RootObject ro = new RootObject();
Cells cs = new Cells();
cs.results = new List<Result>();
Result rt = new Result();
rt.Key = "Title";
rt.Value = "hello";
rt.ValueType = "Edm.String";
cs.results.Add(rt);
Result rs = new Result();
rs.Key = "Size";
rs.Value = "3223";
rs.ValueType = "Edm.Int64";
cs.results.Add(rs);
ro.Cells = cs;
string json = JsonConvert.SerializeObject(ro);
}
this will work
static void Main(string[] args)
{
RootObject ro = new RootObject();
Cells cs = new Cells();
cs.results = new List<Result>();
Result rt = new Result();
rt.Key = "Title";
rt.Value = "hello";
rt.ValueType = "Edm.String";
cs.results.Add(rt);
Result rs = new Result();
rs.Key = "Size";
rs.Value = "3223";
rs.ValueType = "Edm.Int64";
cs.results.Add(rs);
ro.Cells = cs;
string json = JsonConvert.SerializeObject(ro);
}
You're looking for the function DeserializeObject<T>:
var json = ""; // string up above in your code
var jObect = JsonConvert.DeserializeObject<RootObject>(json);
// Use
var cells = jObject.Cells;
var result1 = cells.results.FirstOrDefault();
Given an example of a POCO below:
public class Account
{
public string Email { get; set; }
public bool Active { get; set; }
public DateTime CreatedDate { get; set; }
public IList<string> Roles { get; set; }
}
This can be achieved by deserializing your JSON string show below:
string json = #"{
'Email': 'james#example.com',
'Active': true,
'CreatedDate': '2013-01-20T00:00:00Z',
'Roles': [
'User',
'Admin'
]
}";
Account account = JsonConvert.DeserializeObject<Account>(json);
Console.WriteLine(account.Email);
Reference Newtonsoft's documentation below:
http://www.newtonsoft.com/json/help/html/DeserializeObject.htm
If you want the string representation of an object, especially a json object, the most relevant is .ToString ().
But, it could fail for other reasons ...
In my service class constructor I populate two repositories:
private List<BackupRule> rules;
private List<BackupPath> paths;
public RuleService()
{
List<BackupRule> _rules = new List<BackupRule>();
List<BackupPath> _paths = new List<BackupPath>();
using (var wc = new System.Net.WebClient())
{
string json = wc.DownloadString(jsonRuleFile);
if (json.Length > 0)
{
_rules = JsonConvert.DeserializeObject<List<BackupRule>>(json);
_paths = JsonConvert.DeserializeObject<List<BackupPath>>(json);
}
}
rules = _rules;
paths = _paths;
}
Then I try to create a rule and add the rule to the repository:
code to create the rules
IRuleService db = new RuleService();
List<BackupPath> paths = new List<BackupPath>();
paths.Add(new BackupPath { Type = PathType.Source, Value = "C:\\Test\\" });
paths.Add(new BackupPath { Type = PathType.Destination, Value = "C:\\Test\\" });
paths.Add(new BackupPath { Type = PathType.Source, Value = "C:\\Test\\" });
paths.Add(new BackupPath { Type = PathType.Source, Value = "C:\\Test\\" });
BackupRule rule1 = new BackupRule() { Name = "test1", Type = RuleType.Archive, UserName = System.Environment.UserName, Paths = paths, EndOfDay = true, Created = DateTime.Now};
BackupRule rule2 = new BackupRule() { Name = "test2", Type = RuleType.Archive, UserName = System.Environment.UserName, Paths = paths, EndOfDay = true, Created = DateTime.Now };
db.CreateRule(rule1);
db.CreateRule(rule2);
db.SaveChanges();
Interface Methods that add the rules to the repositories
public BackupRule CreateRule(BackupRule rule)
{
if (rules.Any(r => r.Name == rule.Name))
{
return null;
}
rule.Paths = rule.Paths.OrderBy(p => p.Type.GetHashCode()).ToList();
foreach (BackupPath path in rule.Paths)
{
path.Id = (paths.Count() == 0) ? 1 : paths.LastOrDefault().Id + 1;
paths.Add(path);
}
rule.Id = (rules.Count() == 0) ? 1 : rules.LastOrDefault().Id + 1;
rules.Add(rule);
rules = rules.OrderBy(r => r.Id).ToList();
return rule;
}
Interface Method that serializes (sic?) back to json
public void SaveChanges()
{
using (FileStream fileStream = File.Open(#jsonRuleFile, FileMode.OpenOrCreate))
using (StreamWriter streamWriter = new StreamWriter(fileStream))
using (JsonWriter jsonWriter = new JsonTextWriter(streamWriter))
{
jsonWriter.Formatting = Formatting.Indented;
JsonSerializer serializer = new JsonSerializer();
serializer.Serialize(jsonWriter, rules);
serializer.Serialize(jsonWriter, paths);
}
}
json output
[
{
"Id": 1,
"Name": "test1",
"UserName": "Aaron",
"rule_type": "Archive",
"Paths": [
{
"$id": "1",
"Id": 5,
"Value": "C:\\Test\\",
"Type": "Source"
},
{
"$id": "2",
"Id": 6,
"Value": "C:\\Test\\",
"Type": "Source"
},
{
"$id": "3",
"Id": 7,
"Value": "C:\\Test\\",
"Type": "Source"
},
{
"$id": "4",
"Id": 8,
"Value": "C:\\Test\\",
"Type": "Destination"
}
],
"EndOfDay": true,
"Created": "2014-07-12T20:14:03.9126784-05:00"
},
{
"Id": 2,
"Name": "test2",
"UserName": "Aaron",
"rule_type": "Archive",
"Paths": [
{
"$ref": "1"
},
{
"$ref": "2"
},
{
"$ref": "3"
},
{
"$ref": "4"
}
],
"EndOfDay": true,
"Created": "2014-07-12T20:14:03.9126784-05:00"
}
][
{
"Id": 5,
"Value": "C:\\Test\\",
"Type": "Source"
},
{
"Id": 6,
"Value": "C:\\Test\\",
"Type": "Source"
},
{
"Id": 7,
"Value": "C:\\Test\\",
"Type": "Source"
},
{
"Id": 8,
"Value": "C:\\Test\\",
"Type": "Destination"
},
{
"Id": 5,
"Value": "C:\\Test\\",
"Type": "Source"
},
{
"Id": 6,
"Value": "C:\\Test\\",
"Type": "Source"
},
{
"Id": 7,
"Value": "C:\\Test\\",
"Type": "Source"
},
{
"Id": 8,
"Value": "C:\\Test\\",
"Type": "Destination"
}
]
Classes being serialized
// using statements added to post to help
// reader understand I am using Json.Net
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
public class BackupRule
{
[JsonProperty]
public int Id { get; set; }
[JsonProperty]
public string Name { get; set; }
public string UserName { get; set; }
[JsonProperty(PropertyName = "rule_type")]
[JsonConverter(typeof(StringEnumConverter))]
public RuleType Type { get; set; }
[JsonProperty(ItemIsReference = true)]
public List<BackupPath> Paths { get; set; }
public bool EndOfDay { get; set; }
public DateTime Created { get; set; }
}
public class BackupPath
{
public int Id { get; set; }
public string Value { get; set; }
[JsonConverter(typeof(StringEnumConverter))]
public PathType Type { get; set; }
}
The problem(s) is the json isn't even a proper format it gives me:
Warning 1 Only one top-level array or object is allowed in a JSON document.
And it doesn't actually reference the serialized BackupPath class it just references other BackupPaths created in other BackupRule objects. Where did I go wrong here? Ideally I would like it to create two "tables" out of the lists and have the BackupRule reference the BackupPaths "table" much like it does in the second BackupRule example but instead of BackupRule #2 referencing BackupPath stored in BackupRule #1 I'd rather both reference the serialized BackupPath "table". Also ideally i'd like to store both of the serialized lists in one json file, but I am flexible on this.
As to the warning, that's because you have this:
serializer.Serialize(jsonWriter, rules);
serializer.Serialize(jsonWriter, paths);
As the warning says, JSON really can only have 1 top level element and you have written 2 top level elements. To make the warning go away, you could wrap rules and paths into a single object:
serializer.Serialize(jsonWriter, new {Rules = rules, Paths = paths});