简体   繁体   中英

Adding JObject to JArray

Im trying to add a new class called "Company" to a Json Array called Companies. I'm doing this using C# and Json .net Ive tried many different things. I have them all pares out and in Jobjects ready to be molded together but I can't find a way to do so. Im trying to get it to find "Companies" then insert the new company object in there.

This is what im trying to do.

public void CreateNewCompany()
{
    Company company = new Company
    {
        CompanyName = textBox1.Text,
        IPO = Convert.ToDouble(textBox2.Text),
        Category = CategorycomboBox1.SelectedItem.ToString(),
        Description = textBox4.Text,
        StartDate = Convert.ToInt32(textBox5.Text)
    };

    AddProductListItemsToFinishedJSON(company);
    AddNewsArticlesListItemsToFinishedJSON(company);

    JObject newCompany = JObject.FromObject(company);

    string existingFileContents = File.ReadAllText(path);
    string newFileContents      = newCompany.ToString();          

    var existingFileContentsToJSON = JObject.Parse(existingFileContents);
    var newFileContentsToJSON      = JObject.Parse(newFileContents);

    Debug.WriteLine(existingFileContents);

    SaveJSONFile(company);
}

public void SaveJSONFile(Company localcompany)
{
    if (File.Exists(Path.Combine(@"D:\", "comp.json")))
    {
        File.Delete(Path.Combine(@"D:\", "comp.json"));
    }       

    string RawJSON       = JsonConvert.SerializeObject(localcompany);
    string FormattedJSON = JToken.Parse(RawJSON).ToString(Formatting.Indented);

    //Console.WriteLine(FormattedJSON);
    File.WriteAllText(@"D:\comp.json", FormattedJSON);
}

These are the classes

public class Company
{
    public string CompanyName { get; set; }
    public double IPO { get; set; }
    public string Category { get; set; }
    public string Description { get; set; }
    public int    StartDate { get; set; }

    public List<Product> Products                        = new List<Product>();
    public List<NewsArticle> CompanySpecificNewsArticles = new List<NewsArticle>();
    public List<string> EavesDropperList = new List<string>();
}

public class Product
{
    [JsonProperty("ProductName")]
    public string ProductName { get; set; }
}

public class NewsArticle
{
    [JsonProperty("Type")]
    public string Type { get; set; }

    [JsonProperty("Content")]
    public string Content { get; set; }
}

This is what the Json Looks like and I want to add it to 'Companies'

{
   "Companies":[
      {
         "CompanyName":"",
         "IPO":25.0,
         "Category":"Gaming",
         "Description":"A video game company",
         "StartDate":"1-1-2000",
         "Products":[
            {
               "ProductName":""
            },
            {
               "ProductName":""
            }
         ],
         "CompanySpecificNewsArticles":[
            {
               "Type":"Positive",
               "Content":"This company has had a very good year!"
            },
            {
               "Type":"Negative",
               "Content":"This company has had a very bad year!"
            },
            {
               "Type":"Neutral",
               "Content":"This company is doing okay, I guess"
            }
         ],
         "CompanySpecificEavesdropper":[
            {
               "Type":"Positive",
               "Content":"This company has had a very good year!"
            },
            {
               "Type":"Negative",
               "Content":"This company has had a very bad year!"
            },
            {
               "Type":"Neutral",
               "Content":"This company is doing okay, I guess!"
            }
         ]
      }
      //,
      // Other companies omitted
   ]
}

This should give you an idea of what you should be doing

public void CreateNewCompany()
{
    Company company = new Company
    {
        CompanyName = "New Company",
        IPO = Convert.ToDouble("0.2"),
        Category = "Sample Category",
        Description = "Sample Description",
        StartDate = Convert.ToInt32("2009")
    };

    AddProductListItemsToFinishedJSON(company);
    AddNewsArticlesListItemsToFinishedJSON(company);

    SaveJSONFile(company);

}

public static void SaveJSONFile(Company localcompany)
{
    if (File.Exists(path))
    {
        JObject arr = JObject.Parse(File.ReadAllText(path)));
        (arr["Companies"] as JArray).Add(JToken.FromObject(localcompany));
        string RawJSON = JsonConvert.SerializeObject(arr);
        string FormattedJSON = JToken.Parse(RawJSON).ToString(Formatting.Indented);
        File.WriteAllText(path, FormattedJSON);
    }
    else
    {
        JObject arr = new JObject();
        arr.Add("Companies", new JArray());
        (arr["Companies"] as JArray).Add(JToken.FromObject(localcompany));
        string RawJSON = JsonConvert.SerializeObject(arr);
        string FormattedJSON = JToken.Parse(RawJSON).ToString(Formatting.Indented);
        File.WriteAllText(path, FormattedJSON);
    }
}

Delete your existing json file then run this code. The first run creates the file with one object while subsequent runs adds object to it. You can refactor the code.

Json file will have the format below

{
  "Companies": [
    {
      "Products": [],
      "CompanySpecificNewsArticles": [],
      "EavesDropperList": [],
      "CompanyName": "New Company",
      "IPO": 0.2,
      "Category": "Sample Category",
      "Description": "Sample Description",
      "StartDate": 2009
    },
    {
      "Products": [],
      "CompanySpecificNewsArticles": [],
      "EavesDropperList": [],
      "CompanyName": "New Company",
      "IPO": 0.2,
      "Category": "Sample Category",
      "Description": "Sample Description",
      "StartDate": 2009
    }
  ]
}

as per your comment on the order, you can set order on you class like below

public class Company
{
    [JsonProperty(Order = 0)]
    public string CompanyName { get; set; }
    [JsonProperty(Order = 1)]
    public double IPO { get; set; }
    [JsonProperty(Order = 2)]
    public string Category { get; set; }
    [JsonProperty(Order = 3)]
    public string Description { get; set; }
    [JsonProperty(Order = 4)]
    public int StartDate { get; set; }
    [JsonProperty(Order = 5)]

    public List<Product> Products = new List<Product>();
    [JsonProperty(Order = 6)]
    public List<NewsArticle> CompanySpecificNewsArticles = new List<NewsArticle>();
    [JsonProperty(Order = 7)]
    public List<string> EavesDropperList = new List<string>();
}

A JSON file is just a text file, so there's no straightforward way to insert a record into the middle of the file. Instead, you will need to load the entire file into some in-memory representation, add your Company to the "Companies" array, then re-serialize the file back to disk.

To accomplish this, first create the following extension methods:

public class JsonExtensions
{
    public static T LoadFromFileOrCreateDefault<T>(string path, JsonSerializerSettings settings = null) where T : new()
    {
        var serializer = JsonSerializer.CreateDefault(settings);

        try
        {
            using (var file = File.OpenText(path))
            {
                return (T)JsonSerializer.CreateDefault(settings).Deserialize(file, typeof(T));
            }
        }
        catch (FileNotFoundException)
        {
            return new T();
        }
    }

    public static void SaveToFile<T>(T root, string path, Formatting formatting = Formatting.None, JsonSerializerSettings settings = null)
    {
        using (var file = File.CreateText(path))
        using (var writer = new JsonTextWriter(file) { Formatting = formatting })
        {
            JsonSerializer.CreateDefault(settings).Serialize(writer, root);
        }           
    }
}

Now you can add your Company to the array in CreateNewCompany() as follows:

var root = JsonExtensions.LoadFromFileOrCreateDefault<JObject>(Path);

var companiesArray = (JArray)root["Companies"] ?? (JArray)(root["Companies"] = new JArray());

companiesArray.Add(JObject.FromObject(company));

JsonExtensions.SaveToFile(root, Path, Formatting.Indented);

Demo fiddle #1 here .

Incidentally, since your entire file seems to have a fixed schema, you could simplify your code and get slightly better performance by deserializing directly to some root data model, omitting the JObject representation entirely.

First, create the following root data model:

public class CompanyList
{
    public List<Company> Companies { get; } = new List<Company>();
}

Then modify CreateNewCompany() as follows:

var root = JsonExtensions.LoadFromFileOrCreateDefault<CompanyList>(Path);

root.Companies.Add(company);

JsonExtensions.SaveToFile(root, Path, Formatting.Indented);

Demo fiddle #2 here .

Notes:

  • By using generics in JsonExtensions we can use the same code to load from, and save to, a file, for both JObject and CompanyList .

  • Serializing directly from and to your file without loading to an intermediate string should improve performance as explained in Performance Tips: Optimize Memory Usage .

  • Company.StartDate is declared to be an int , however in your JSON it appears as a non-numeric string :

     "StartDate": "1-1-2000" 

    You will need to adjust your data model to account for this.

  • There is no need to manually delete the old file as File.CreateText(String) creates or opens a file for writing UTF-8 encoded text. If the file already exists, its contents are overwritten.

    Alternatively you might want to write to a temporary file and then overwrite the old file only after serialization finishes successfully.

  • It is better to catch the FileNotFoundException from File.OpenText() rather than checking File.Exists() manually in case the file is somehow deleted in between the two calls.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM