I believe I checked all the recursive JSON sections on here, none seem even recursive but whatever. I have a challenge. How do I iterate through all the depts of this JSON? This JSON has a depth of 200 and that's not even the biggest one.
The JSON "Recursive" is not even a true array, there's only 1 item.
{
"schema": [
{
"#id": 1,
"recursive": [
{
"#Id": 2,
"from": {
"#id": 1,
"recursive": [
{
"#Id": 2,
"from": {
"#id": 1,
"recursive": [
"#Id": 2,
"from" : 49
]
}
}
]
}
}
]
}
]
}
My Configuration classes
public class rootSchema
{
[JsonProperty("#id")]
public int #id { get; set; }
[JsonProperty("recursive")]
public List<recursive> recursive { get; set; }
}
public class from
{
[JsonProperty("#id")]
public int #id { get; set; }
[JsonProperty("recursive")]
public List<recursive> recursive { get; set; }
}
public class recursive
{
[JsonProperty("from")]
public from from { get; set; }
[JsonProperty("recursive")]
public List<recursive> recursive { get; set; }
}
When I attempt to deserialize this using NewtonSoft.JSON Deserializer, I get an int64 conversion error
Newtonsoft.Json.JsonSerializationException:
'Error converting value 49 to type 'Proj.recursive.from'. Path
'recursive[0].from.recursive[0].from.recursive[0].from.recursive[0]
.from.recursive[0].from.recursive[0].from.recursive[0].from.recursive[0]
.from.recursive[0].from.recursive[0].from.recursive[0].from.recursive[0]
.from.recursive[0].from.recursive[0].from.recursive[0].from.recursive[0]
.from.recursive[0].from.recursive[0].from.recursive[0].from.recursive[0]
.from.recursive[0].from.recursive[0].from.recursive[0].from.recursive[0]
.from.recursive[0].from', line 1, position 16273.'
I found the cause of this, because in the last element "from", its not a list, its an integer
"from": 49,
So I am not even sure how to account for a list, & and integer in my definitions. My second issue is, I'm not sure how to iterate through the recursion to get all the "recursive" elements.
Solved it. Im just going to leave this here
Defs
using Newtonsoft.Json;
using System.Collections.Generic;
namespace theHack
{
public class Root
{
[JsonProperty("ratsnest")]
public ratsnest ratsnest { get; set; }
}
public class ratsnest
{
[JsonProperty("garbagepile")]
public List<object> garbagepile { get; set; }
}
public class garbagepile
{
[JsonProperty("#id")]
public int #id { get; set; }
[JsonProperty("hammeredhotpoop")]
public List<object> hammeredhotpoop { get; set; }
}
public class hammeredhotpoop
{
[JsonProperty("#transitionId")]
public int #transitionId { get; set; }
[JsonProperty("nohumorleft")]
public object nohumorleft { get; set; }
}
public class outgoingTransitions
{
[JsonProperty("#transitionId")]
public int #transitionId { get; set; }
[JsonProperty("nohumorleft")]
public object nohumorleft { get; set; }
}
public class nohumorleft
{
[JsonProperty("#id")]
public int #id { get; set; }
[JsonProperty("hammeredhotpoop")]
public List<object> hammeredhotpoop { get; set; }
}
}
The "logic"... it compiles
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
namespace theHack
{
internal class Program
{
static void Main(string[] args)
{
string _data_file = GetFile();
// going normal, this is expected
Root root = JsonConvert.DeserializeObject<Root>(_data_file, new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All, MaxDepth = 200, NullValueHandling = NullValueHandling.Ignore });
// can only be 1 root state, everything goes downhill from here
var parent = root.ratsnest.garbagepile[0];
//strip all the stupid ids, must repeat this because any element can return any type
var parentSerialized = JsonConvert.SerializeObject(parent);
// begin the descent to only lord knows where
garbagepile handful = JsonConvert.DeserializeObject<garbagepile>(parentSerialized, new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All, MaxDepth = 200, NullValueHandling = NullValueHandling.Ignore });
// Can be integer or Jobject, who the heck knows, maybe a trainwreck managed to find its way here
List<object> incoming = handful.hammeredhotpoop;
foreach (var crap in incoming)
{
// whyyyyyyyyyyyyyyyy
if (crap.GetType() != typeof(int))
{
var rootOfEvil = JsonConvert.SerializeObject(crap);
// thank you James Newton King for making the longest constructor I've ever seen
hammeredhotpoop firstSeedOfEvil = JsonConvert.DeserializeObject<hammeredhotpoop>(rootOfEvil, new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All, MaxDepth = 200, NullValueHandling = NullValueHandling.Ignore });
Console.WriteLine($"#transitionId: {firstSeedOfEvil.transitionId}");
var firstHorseman = JsonConvert.SerializeObject(firstSeedOfEvil.nohumorleft);
nohumorleft forgotwhatIwasDoing = JsonConvert.DeserializeObject<nohumorleft>(firstHorseman, new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All, MaxDepth = 200, NullValueHandling = NullValueHandling.Ignore });
List<object> currentCrap = forgotwhatIwasDoing.hammeredhotpoop;
Console.WriteLine($"#id: {forgotwhatIwasDoing.#id}");
//loop
do
{
for (int i = 0; i < currentCrap.Count; i++)
{
//deserialize the List<object>
hammeredhotpoop omgIgiveup = updatemyresume(currentCrap[i]);
//output something
Console.WriteLine($"#transitionId: {omgIgiveup.transitionId}");
// create typed version from this current Transition
nohumorleft deserializedfirstHorseman = barreloflaughs(omgIgiveup.nohumorleft);
Console.WriteLine($"#id: {deserializedfirstHorseman.#id}");
//get the next set of incomingTansitions
currentCrap = deserializedfirstHorseman.hammeredhotpoop;
}
} while (currentCrap != null);
}
}
Console.ReadLine();
}
public static hammeredhotpoop updatemyresume(object garbageIn)
{
if (garbageIn.GetType() != typeof(int))
{
var hammerIt = JsonConvert.SerializeObject(garbageIn);
hammeredhotpoop garbageOut = JsonConvert.DeserializeObject<hammeredhotpoop>(hammerIt, new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All, MaxDepth = 200, NullValueHandling = NullValueHandling.Ignore });
return garbageOut;
}
else { return null; }
}
public static nohumorleft barreloflaughs(object garbageIn)
{
var hammerIt = JsonConvert.SerializeObject(garbageIn);
nohumorleft garbageOut = JsonConvert.DeserializeObject<nohumorleft>(hammerIt, new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All, MaxDepth = 200, NullValueHandling = NullValueHandling.Ignore });
return garbageOut;
}
public static string GetFile()
{
var fileName = #"C:\Users\me\source\test.json";
if (File.Exists(fileName))
{
using (StreamReader file = new StreamReader(fileName))
{
string text = File.ReadAllText(fileName);
file.Close();
return text;
}
}
else
{
Console.WriteLine("File does not exist");
return null;
}
}
}
}
a representation of the JSON
{
"ThisWasActuallyAnArray": [
{
"surprising": [
{
"typeNull": null,
"connectionId": "233ad541-7468-49b5-a7bb-dddddd",
"id": 29531,
"name": "funnyTown",
"letsEmbedSomeJson": "{\"id\":\"234\",..."
}
],
"type": "",
"id": 46243,
"name": "somestring"
}
],
"ratsnest": {
"whynot": null,
"garbagepile": [
{
"#id": 1,
"subData": null,
"subDataClass": "",
"id": "123-abc",
"hammeredhotpoop": [
{
"#transitionId": 4,
"datastuff": {
"abool": false,
"astring": "anyway",
"anInt": 1
},
"anotherRabbitHole2": 3,
"id": "123",
"name": "...",
"anotherRabbitHole": {
"#id": 5,
"someData": {
"setting1": true,
"Grouping": "FooBar",
"str1": "60",
"aBool": true,
"id": 5
},
"config": "somedata",
"id": "123",
"nohumorleft": {},
"name": "",
"notes": ""
},
"nohumorleft": {
"#id": 9,
"listData": {},
"id": "123",
"hammeredhotpoop": [
{
"#transitionId": 4,
"datastuff": {
"abool": false,
"astring": "anyway",
"anInt": 1
},
"anotherRabbitHole2": 3,
"id": "123",
"name": "...",
"anotherRabbitHole": {
"#id": 5,
"someData": {
"setting1": true,
"Grouping": "FooBar",
"str1": "60",
"aBool": true,
"id": 5
},
"config": "somedata",
"id": "123",
"nohumorleft": {},
"name": "",
"notes": ""
},
"nohumorleft": [
{
"#id": 9,
"listData": {},
"id": "123",
"hammeredhotpoop": [
{
"#transitionId": 4,
"datastuff": {
"abool": false,
"astring": "anyway",
"anInt": 1
},
"anotherRabbitHole2": 3,
"id": "123",
"name": "...",
"anotherRabbitHole": {
"#id": 5,
"someData": {
"setting1": true,
"Grouping": "FooBar",
"str1": "60",
"aBool": true,
"id": 5
},
"config": "somedata",
"id": "123",
"nohumorleft": {},
"name": "",
"notes": ""
},
"nohumorleft": {
"#id": 9,
"listData": {},
"id": "123",
"hammeredhotpoop": [
{
"#transitionId": 4,
"datastuff": {
"abool": false,
"astring": "anyway",
"anInt": 1
},
"anotherRabbitHole2": 3,
"id": "123",
"name": "...",
"anotherRabbitHole": {
"#id": 5,
"someData": {
"setting1": true,
"Grouping": "FooBar",
"str1": "60",
"aBool": true,
"id": 5
},
"config": "somedata",
"id": "123",
"name": "",
"notes": "",
"nohumorleft": 49
}
}
]
}
}
]
},
"3",
"2",
"1",
"upskill",
true
]
}
]
}
},
5,
4,
3,
2,
1,
"LOL"
],
"notes": "",
"someInt": 0,
"type": "",
"hammeredhotboogers": [],
"listData": {}
}
]
}
}
Related
I have a model class that allows users to specify the template which is used to create content with specified properties.
public class Model
{
public string Id { get; set; }
public string Alias { get; set; }
public ICollection<Field> Fields { get; set; }
}
public class Field
{
public FieldType Type { get; set; }
public string Alias { get; set; }
}
public enum FieldType
{
String = 1,
Email = 2,
Password = 3,
DateTime = 4,
Integer = 5,
Decimal = 6,
...
}
The content that is created based on this template will have the structure as below:
public class Entry
{
public string _id { get; set; }
public List<FieldValue> FieldValues { get; set; }
}
public class FieldValue
{
public object Value { get; set; }
public string Alias { get; set; }
public FieldType Type { get; set; }
}
So, I for example, can have data such as:
PersonModel
{
"id": "34752b15",
"alias": "person",
"fields": [
{
"type": 1,
"alias": "name"
},
{
"type": 2,
"alias": "email"
}
]
}
PersonEntries:
[
{
"_id": "426e",
"fieldValues": [
{
"alias": "name",
"value": "John",
"type": 1
},
{
"alias": "email",
"value": "john#stark.com",
"type": 2
}
]
},
{
"_id": "c6110fc3cc0e",
"fieldValues": [
{
"alias": "name",
"value": "Arya",
"type": 1
},
{
"alias": "email",
"value": "arya#stark.com",
"type": 2
}
]
}
]
Or:
ShapeModel
{
"id": "afd69af4",
"alias": "shape",
"fields": [
{
"type": 1,
"alias": "name"
},
{
"type": 6,
"alias": "area"
},
{
"type": 6,
"alias": "perimeter"
}
]
}
ShapeEntries:
[
{
"_id": "426e",
"fieldValues": [
{
"alias": "name",
"value": "my-square",
"type": 1
},
{
"alias": "area",
"value": 1,
"type": 6
},
{
"alias": "perimeter",
"value": 4,
"type": 6
}
]
},
{
"_id": "426enbv",
"fieldValues": [
{
"alias": "name",
"value": "my-triangle",
"type": 1
},
{
"alias": "area",
"value": 0.5,
"type": 6
},
{
"alias": "perimeter",
"value": 3,
"type": 6
}
]
}
]
and so on.
The idea is to create graphql schemas dynamically (on runtime) when a new model is created and allow users to query entries based on the model alias.
What I tried is:
public ISchema CreateEntrySchema(List<Entry> entries, Model model, IServiceCollection services)
{
var entryType = new ObjectGraphType() { Name = model.Alias };
entryType.Field("_id", new IdGraphType());
foreach (var field in model.Fields)
{
if (entryType.Fields.FirstOrDefault(f => f.Name == field.Alias) != null)
{
continue;
}
Type clrGraphType = field.Type.GetStandardType().GetGraphTypeFromType(isNullable: true);
IGraphType graphType = (IGraphType)Activator.CreateInstance(clrGraphType);
entryType.Field(field.Alias,
graphType,
resolve: context =>
{
var entry = (Entry)context.Source;
object firstOrDefault = entry?.FieldValues.FirstOrDefault(value => value.Alias == context.FieldDefinition.Name)?.Value;
return firstOrDefault;
});
}
var queryType = new ObjectGraphType();
queryType.Field(
model.Alias,
type: new ListGraphType(entryType),
arguments: new QueryArguments(ConstructArguments(model.Fields)),
resolve: context =>
{
List<KeyValuePair<string, ArgumentValue>> args = context.Arguments?.Where(pair => pair.Value.Value != null).ToList();
if (!args?.Any() ?? true)
{
return entries;
}
return args.Aggregate(entries, (current, pair) =>
current.Where(entry => entry.FieldValues.FirstOrDefault(field => field.Alias == pair.Key) != null && entry.FieldValues.FirstOrDefault(field => field.Alias == pair.Key).Value.Equals(pair.Value.Value))
.ToList());
});
ISchema schema = new Schema { Query = queryType };
schema.RegisterType(entryType);
services.AddGraphQL(a =>
{
a.AddSchema(schema).AddClrTypeMappings();
});
return schema;
}
The problem is that it is registering only the last schema. So, for example, if I will call this method like:
CreateEntrySchema(personEntries, personModel, services);
CreateEntrySchema(shapeEntries, shapeModel, services);
And query like this:
{
shape{
name
}
person{
name
}
}
The result is:
"GraphQL.Validation.Errors.FieldsOnCorrectTypeError: Cannot query field 'person' on type 'Object'. Did you mean 'shape'?",
Given 2 JSON strings:
[
{
"id":"BA",
"description":"BrandA",
"values":[
{
"id":"CategoryA",
"description":"CategoryA"
},
{
"id":"CategoryB",
"description":"CategoryB"
},
{
"id":"CategoryC",
"description":"CategoryC"
},
{
"id":"CategoryD",
"description":"CategoryD"
},
{
"id":"CategoryE",
"description":"CategoryE"
},
{
"id":"CategoryF",
"description":"CategoryF"
},
{
"id":"CategoryG",
"description":"CategoryG"
},
{
"id":"CategoryH",
"description":"CategoryH"
}
]
},
{
"id":"BB",
"description":"BrandB",
"values":[
{
"id":"CategoryA",
"description":"CategoryA"
},
{
"id":"CategoryB",
"description":"CategoryB"
},
{
"id":"CategoryC",
"description":"CategoryC"
}
]
}
]
AND
[
{
"id":"BA",
"description":"BrandA",
"values":[
{
"id":"CategoryA",
"description":"CategoryA"
},
{
"id":"CategoryC",
"description":"CategoryC"
}
]
},
{
"id":"BB",
"description":"BrandB",
"values":[
{
"id":"CategoryB",
"description":"CategoryB"
}
]
}
]
First one is the original. The second are the values that I want to remove from the original. So basically, if there is a match on brand and category between first and second JSON, regardless of the order of the elements, I want that match to be removed.
The expected result would be someting like this:
[
{
"id":"BA",
"description":"BrandA",
"values":[
{
"id":"CategoryB",
"description":"CategoryB"
},
{
"id":"CategoryD",
"description":"CategoryD"
},
{
"id":"CategoryE",
"description":"CategoryE"
},
{
"id":"CategoryF",
"description":"CategoryF"
},
{
"id":"CategoryG",
"description":"CategoryG"
},
{
"id":"CategoryH",
"description":"CategoryH"
}
]
},
{
"id":"BB",
"description":"BrandB",
"values":[
{
"id":"CategoryA",
"description":"CategoryA"
},
{
"id":"CategoryC",
"description":"CategoryC"
}
]
}
]
Catagory A and C in Brand A were removed as well as Category B in Brand B.
Based in some research, I was using https://github.com/wbish/jsondiffpatch.net, tried to work with it's functions, but so far I didn't manage to achieve the result I want. Also, to solve this by processing JSON direcly is not a must. If there is a simpler solution to achieve that by converting them to lists and use something like LINQ for example, it works for me as well (tried that, but didn't manage to find a way to do this comparison).
Thanks in advance.
If performance does not matter, the JsonSerializer and LINQ can be used:
Model
public class JsonModel
{
public class ValueModel
{
public string Id { get; set; }
public string Description { get; set; }
}
public string Id { get; set; }
public string Description { get; set; }
public IEnumerable<ValueModel> Values { get; set; }
}
Deserialize and LINQ
string json1Str = #"[...]";
string json2Str = #"[...]";
var opt = new System.Text.Json.JsonSerializerOptions { PropertyNameCaseInsensitive = true };
var json1 = System.Text.Json.JsonSerializer.Deserialize<List<JsonModel>>(json1Str, opt);
var json2 = System.Text.Json.JsonSerializer.Deserialize<List<JsonModel>>(json2Str, opt);
var result = json1.
Join(json2, j1 => j1.Id, j2 => j2.Id, (j1, j2) => new JsonModel
{
Id = j1.Id,
Description = j1.Description,
Values = j1.Values.Where(j1 => !j2.Values.Select(val => val.Id).Contains(j1.Id))
});
Console.WriteLine(System.Text.Json.JsonSerializer.Serialize(result, new System.Text.Json.JsonSerializerOptions { WriteIndented = true }));
}
Result
[
{
"Id": "BA",
"Description": "BrandA",
"Values": [
{
"Id": "CategoryB",
"Description": "CategoryB"
},
{
"Id": "CategoryD",
"Description": "CategoryD"
},
{
"Id": "CategoryE",
"Description": "CategoryE"
},
{
"Id": "CategoryF",
"Description": "CategoryF"
},
{
"Id": "CategoryG",
"Description": "CategoryG"
},
{
"Id": "CategoryH",
"Description": "CategoryH"
}
]
},
{
"Id": "BB",
"Description": "BrandB",
"Values": [
{
"Id": "CategoryA",
"Description": "CategoryA"
},
{
"Id": "CategoryC",
"Description": "CategoryC"
}
]
}
]
Hi I try to get json data inside of the json. But my class is Employee my service creates json as com.myteam.rbffiyatlama2.Employee this prefix can be changeable so I have to write a solution to get an exact part of the json Like below but my below code is not working. I will send my node name to a method Getjsonobject(Employee emp) or Getjsonobject(Customer cust) or Getjsonobject(Student student) etc.
My Json:
{
"type": "SUCCESS",
"msg": "Container RBFFiyatlama2_1.0.1 successfully called.",
"result": {"execution-results": {
"results": [
{
"value": 2,
"key": ""
},
{
"value": {"com.myteam.rbffiyatlama2.Employee": {
"salary": 2400,
"age": 35,
"cofactor": 0.2
}},
"key": "t1"
},
{
"value": {"com.myteam.rbffiyatlama2.Employee": {
"salary": 4800,
"age": 35,
"cofactor": 0.2
}},
"key": "t2"
}
],
"facts": [
{
"value": {"org.drools.core.common.DefaultFactHandle": {"external-form": "0:50:1980606587:1980606587:100:DEFAULT:NON_TRAIT:com.myteam.rbffiyatlama2.Employee"}},
"key": "t1"
},
{
"value": {"org.drools.core.common.DefaultFactHandle": {"external-form": "0:51:2052360932:2052360932:99:DEFAULT:NON_TRAIT:com.myteam.rbffiyatlama2.Employee"}},
"key": "t2"
}
]
}}
}
class Program
{
static void Main(string[] args)
{
var employee1 = new Employee() { age = 35, cofactor = 0.2, salary = 2000 };
var employee2 = new Employee() { age = 35, cofactor = 0.2, salary = 4000 };
var list = new List<Employee>();
list.Add(employee1);
list.Add(employee2);
var uri = new Uri("http://localhost:8080/kie-server/services/rest/server/containers/instances/RBFFiyatlama2_1.0.1");
var kieclient = new KieRequestWrapper<Employee>(uri, "kieserver", "#test2018", MethodType.POST, "application/json").Add(list).Run();
Console.Write(kieclient.Content);
var match = Regex.Match(kieclient.Content, #"(?*.Employee{*})");
var result= MyParser.Parse(match, typeof(Employee)); //Desired
Console.Read();
}
}
public class Employee
{
public int age { get; set; }
public double cofactor { get; set; }
public int salary { get; set; }
}
You don't want to use XPath to get the data you need, you want to deserialize the the JSON string into an object and then get the data you need. There are many JSON serialization libraries out there, the most common one, AFAIK, is JSON.NET. You can look at how deserialization works here: https://www.newtonsoft.com/json/help/html/DeserializeObject.htm
Example:
public class Account
{
public string Email { get; set; }
public bool Active { get; set; }
public DateTime CreatedDate { get; set; }
public IList<string> Roles { get; set; }
}
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);
// james#example.com
So here is my JSON:
[
{
"Assignees": [
{
"ID": "1111",
"IsPrimaryOffice": true
},
{
"ID": "2222",
"IsPrimaryOffice": false
}
],
"Height": "76",
"Width": "78",
"Top": "160",
"Left": "99.5"
},
{
"Assignees": [
{
"ID": "3333",
"IsPrimaryOffice": true
},
{
"ID": "4444",
"IsPrimaryOffice": false
}
],
"Height": "11",
"Width": "11",
"Top": "11",
"Left": "11"
},
{
"Assignees": null,
"Height": "22",
"Width": "22",
"Top": "22",
"Left": "22"
},
]
Where each main object in my array holds an array of sub-objects "Assignees".
So what I'm trying to do is to search each "Assignees" object in its array for a match on ID.
For example: I want to find the Assignee object which has an ID of "3333" and also has a true value for IsPrimaryOffice with LINQ. How can I do that? Here is what I came up with, but it always returns null:
mainObject.Where(x =>
x.Assignees != null &&
x.Assignees.Any(y => y.ID == "3333" && y.IsPrimaryOffice == true))
.FirstOrDefault();
Can anyone help me out here? Thank you in advance
I have created class using json2sharp for provided json :
var jsonArray = "[{\"Assignees\":[{\"ID\": \"1111\",\"IsPrimaryOffice\": true },
{\"ID\": \"2222\",\"IsPrimaryOffice\": false } ],
\"Height\": \"76\", \"Width\": \"78\", \"Top\": \"160\", \"Left\": \"99.5\" },
{ \"Assignees\": [ {\"ID\": \"3333\",\"IsPrimaryOffice\": true },
{\"ID\": \"4444\",\"IsPrimaryOffice\": false } ],
\"Height\": \"11\", \"Width\": \"11\", \"Top\": \"11\", \"Left\": \"11\" }]";
which generated as :
public class Assignee
{
public string ID { get; set; }
public bool IsPrimaryOffice { get; set; }
}
public class RootObject
{
public List<Assignee> Assignees { get; set; }
public string Height { get; set; }
public string Width { get; set; }
public string Top { get; set; }
public string Left { get; set; }
}
Now when I ran the below query
var rootObj = JsonConvert.DeserializeObject<JArray>(jsonArray).ToObject<List<RootObject>>().Where(x =>
x.Assignees != null &&
x.Assignees.Any(y => y.ID == "3333" && y.IsPrimaryOffice == true))
.FirstOrDefault();
foreach (var assignee in rootObj.Assignees)
{
Console.WriteLine("ID = " + assignee.ID);
Console.WriteLine("IsPrimaryOffice = " + assignee.IsPrimaryOffice);
}
then got the output as
ID = 3333
IsPrimaryOffice = True
ID = 4444
IsPrimaryOffice = False
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});