11
string json = "{\"People\":[{\"FirstName\":\"Hans\",\"LastName\":\"Olo\"}
                            {\"FirstName\":\"Jimmy\",\"LastName\":\"Crackedcorn\"}]}";

var obj = JObject.Parse(json);

List<string> first;
List<string> last;

foreach (var child in obj["People"].Children())
{
    var name = child.First()["countryName"].ToString();
    var two = child.First()["countryCode"].ToString();
    var three = child.First()["isoAlpha3"].ToString();

    countries.Add(name);
    twoCharCodes.Add(two);
    threeCharCodes.Add(three);

    Console.Write("Name:\t\t{0}\n2CharCode:\t{1}\n3CharCode:\t{2}\n\n", name, two, three);
}

I'm looking for a way to add each FirstName value into the first List and the same with the LastName vaues and the last List. What is the best way to go about doing this?

The above code breaks on:

var name = child.First()["countryName"].ToString();

with this error:

 Cannot access child value on Newtonsoft.Json.Linq.JProperty

Any advice?

Craig W.
  • 17,838
  • 6
  • 49
  • 82
ChangeJar
  • 163
  • 1
  • 1
  • 10

5 Answers5

19

Seems like a bad way to do it (creating two correlated lists) but I'm assuming you have your reasons.

I'd parse the JSON string (which has a typo in your example, it's missing a comma between the two objects) into a strongly-typed object and then use a couple of LINQ queries to get the two lists.

void Main()
{
    string json = "{\"People\":[{\"FirstName\":\"Hans\",\"LastName\":\"Olo\"},{\"FirstName\":\"Jimmy\",\"LastName\":\"Crackedcorn\"}]}";

    var result = JsonConvert.DeserializeObject<RootObject>(json);

    var firstNames = result.People.Select (p => p.FirstName).ToList();
    var lastNames = result.People.Select (p => p.LastName).ToList();
}

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

public class RootObject
{
    public List<Person> People { get; set; }
}
Craig W.
  • 17,838
  • 6
  • 49
  • 82
  • Awesome, this worked great! Thanks a bunch. I'm making two lists to eventually write the values to a database. I figured this way I can easily accomplish this with a simple `for` loop. Do you have any alternatives? – ChangeJar Aug 28 '15 at 15:16
10

Since you are using JSON.NET, personally I would go with serialization so that you can have Intellisense support for your object. You'll need a class that represents your JSON structure. You can build this by hand, or you can use something like json2csharp to generate it for you:

e.g.

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

public class RootObject
{
    public List<Person> People { get; set; }
}

Then, you can simply call JsonConvert's methods to deserialize the JSON into an object:

RootObject instance = JsonConvert.Deserialize<RootObject>(json);

Then you have Intellisense:

var firstName = instance.People[0].FirstName;
var lastName = instance.People[0].LastName;
Kenneth K.
  • 2,987
  • 1
  • 23
  • 30
  • You mean basically the same answer I posted two minutes before yours? :-) – Craig W. Aug 28 '15 at 14:53
  • 2
    Sure. I was still typing as you were posting. I did give credit to the website that you also used to generate your classes ; ) – Kenneth K. Aug 28 '15 at 15:06
  • @KennethK.: Getting **Null** with this code `RootObject instance = JsonConvert.DeserializeObject(json);` My **json** string is `{\r\n \"People\":[\r\n {\r\n "{\r\n \"People\": [\r\n {\r\n \"FirstName\": \"Hans\",\r\n \"LastName\": \"Olo\"\r\n },\r\n {\r\n \"FirstName\": \"Jimmy\",\r\n \"LastName\": \"Crackedcorn\"\r\n },\r\n {\r\n \"FirstName\": \"Tim\",\r\n \"LastName\": \"John\"\r\n }\r\n ]\r\n} \r\n\r\n"` – venkat Sep 18 '19 at 08:52
  • @venkat Your JSON doesn't match the structure that this question's code represents. And this should probably be a new question. – Kenneth K. Sep 18 '19 at 12:40
1

I use this JSON Helper class in my projects. I found it on the net a year ago but lost the source URL. So I am pasting it directly from my project:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Runtime.Serialization.Json;
using System.IO;
using System.Text;
/// <summary>
/// JSON Serialization and Deserialization Assistant Class
/// </summary>
public class JsonHelper
{
    /// <summary>
    /// JSON Serialization
    /// </summary>
    public static string JsonSerializer<T> (T t)
    {
        DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(T));
        MemoryStream ms = new MemoryStream();
        ser.WriteObject(ms, t);
        string jsonString = Encoding.UTF8.GetString(ms.ToArray());
        ms.Close();
        return jsonString;
    }
    /// <summary>
    /// JSON Deserialization
    /// </summary>
    public static T JsonDeserialize<T> (string jsonString)
    {
        DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(T));
        MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(jsonString));
        T obj = (T)ser.ReadObject(ms);
        return obj;
    }
}

You can use it like this: Create the classes as Craig W. suggested.

And then deserialize like this

RootObject root = JSONHelper.JsonDeserialize<RootObject>(json);
Aishwarya Shiva
  • 3,460
  • 15
  • 58
  • 107
  • My understanding is that the `DataContractJsonSerializer` is much slower than JSON.NET's serializer, which--if memory serves--is why the ASP.NET team switched to JSON.NET for the default JSON serializer. Speed may not be an issue here, but may be of future consideration. – Kenneth K. Aug 28 '15 at 15:09
  • 1
    @KennethK. yeah it depends on project scenario. Like in my case I never had performance issues with this code. It's just another option for OP. Rest depends on his project demands. – Aishwarya Shiva Aug 28 '15 at 15:14
  • Excellent solution! But it should have been mentioned for new users that the class to be serialized must be annotated with attribute [DataContract] and all its properties to be serialized with attribute [DataMember], and may be the ones that you don't want to serialize with [IgnoreDataMember] attribute. – Dia Sheikh May 21 '20 at 09:07
  • @Kenneth K. NewtonSoft's dll takes around 700 kb. In my case, system's memory WAS of concern. Plus Newtonsoft.json (or JSon.Net) was throwing exception at target system while this code embedded inside my project being a regular class. But your comment is very important to make users conscious about performance vs memory compromise. – Dia Sheikh May 21 '20 at 09:16
1

Wanted to post this as a comment as a side note to the accepted answer, but that got a bit unclear. So purely as a side note:

If you have no need for the objects themselves and you want to have your project clear of further unused classes, you can parse with something like:

var list = JObject.Parse(json)["People"].Select(el => new { FirstName = (string)el["FirstName"], LastName = (string)el["LastName"] }).ToList();

var firstNames = list.Select(p => p.FirstName).ToList();
var lastNames = list.Select(p => p.LastName).ToList();

Even when using a strongly typed person class, you can still skip the root object by creating a list with JObject.Parse(json)["People"].ToObject<List<Person>>() Of course, if you do need to reuse the objects, it's better to create them from the start. Just wanted to point out the alternative ;)

Me.Name
  • 12,259
  • 3
  • 31
  • 48
0

Try this:

using System;
using Newtonsoft.Json;
using System.Collections.Generic;
public class Program
{
    public static void Main()
    {
        List<Man> Men = new List<Man>();

        Man m1 = new Man();
        m1.Number = "+1-9169168158";
        m1.Message = "Hello Bob from 1";
        m1.UniqueCode = "0123";
        m1.State = 0;

        Man m2 = new Man();
        m2.Number = "+1-9296146182";
        m2.Message = "Hello Bob from 2";
        m2.UniqueCode = "0125";
        m2.State = 0;

        Men.AddRange(new Man[] { m1, m2 });

        string result = JsonConvert.SerializeObject(Men);
        Console.WriteLine(result);  

        List<Man> NewMen = JsonConvert.DeserializeObject<List<Man>>(result);
        foreach(Man m in NewMen) Console.WriteLine(m.Message);
    }
}
public class Man
{
    public string Number{get;set;}
    public string Message {get;set;}
    public string UniqueCode {get;set;}
    public int State {get;set;}
}