-5

I have this JSON:

{
    "response":
    {
        "data":
        [
            {
                "start":1,
                "subjects":["A"]
            },
            {
                "start":3,
                "subjects":["B"]
            },
            {
                "start":2,
                "subjects":["C"]
            }
        ]
    }
}

And I want to get only the "subject" data from the object with it's "start" value to be the smallest one that is greater than 1.3, which in this case would be C. Would anybody happen to know how such a thing can be achieved using C#?

user5664615
  • 385
  • 1
  • 9
  • 15
CodingMage
  • 135
  • 1
  • 13
  • just want to point out the first one with a `start` value greater than `1.3` is `B` – Technivorous Oct 17 '18 at 18:38
  • 1
    Possible duplicate of [Deserialize JSON into C# dynamic object?](https://stackoverflow.com/questions/3142495/deserialize-json-into-c-sharp-dynamic-object) – Technivorous Oct 17 '18 at 18:39
  • -this should be a comment- possible duplicate of https://stackoverflow.com/questions/19438472/json-net-deserialize-a-specific-property – RuNe Oct 17 '18 at 18:48

2 Answers2

3

I want to extend a bit on the other answers and shed more light into the subject.

A JSON -- JavaScript Object Notation - is just a way to move data "on a wire". Inside .NET, you shouldn't really consider your object to be a JSON, although you may colloquially refer to a data structure as such.

Having said that, what is a JSON "inside" .NET? It's your call. You can, for instance treat it as a string, but you will have a hard time doing this operation of finding a specific node based on certain parameters/rules.

Since a JSON is a tree-like structure, you could build your on data structure or use the many available on the web. This is great if you are learning the workings of the language and programming in general, bad if you are doing this professionally because you will probably be reinventing the wheel. And parsing the JSON is not a easy thing to do (again, good exercise).

So, the most time-effective way of doing? You have two options:

  1. Use a dynamic object to represent your JSON data. A dynamic is a "extension" to .NET (actually, an extension to the CLR, that is called DLR) which lets you create objects that doesn't have classes (they can be considered to be "untyped", or, better, to use duck typing).

  2. Use a typed structure that you defined to hold your data. This is the canonical, object-oriented, .NET way of doing it, but there's a trade-off in declaring classes and typing everything, which is costly in terms of time. The payoff is that you get better intellisense, performance (DLR objects are slower than traditional objects) and more safe code.


To go with the first approach, you can refer to @YouneS answer. You need to add a dependency to your project, Newtonsoft.Json (a nuget), and call deserialize to convert the JSON string to a dynamic object. As you can see from his answer, you can access properties in this object as you would access then on a JavaScript language. But you'll also realize that you have no intellisense and things such as myObj.unexistentField = "asd" will be allowed. That is the nature of dynamic typed objects.

The second approach is to declare all types. Again, this is time consuming and on many cases you'll prefer not to do it. Refer to Microsoft Docs to get more insight.

You should first create your data contracts, as below (forgive me for any typos, I'm not compiling the code).

[DataContract]
class DataItem 
{
    [DataMember]
    public string Start { get; set; }

    [DataMember]
    public string[] Subjects { get; set; } 
}

[DataContract]
class ResponseItem
{
    [DataMember]
    public DateItem[] Data { get; set; }
}

[DataContract]
class ResponseContract 
{
    [DataMember]
    public ResponseItem Response { get; set; }
}

Once you have all those data structures declared, deserialize your json to it:

 using (var ms = new MemoryStream(Encoding.Unicode.GetBytes(json)))
 {
     var deserializer = new DataContractJsonSerializer(typeof(ResponseContract));
     return (T)deserializer.ReadObject(ms);
 }

The code above may seem a bit complicated, but follow a bit of .NET / BCL standards. The DataContractJsonSerializer work only with streams, so you need to open a stream that contains your string. So you create a memory stream with all the bytes from the json string.

You can also use Newtonsoft to do that, which is much simpler but, of course, still requires that extra dependency:

DataContract contract = JsonConvert.DeserializeObject<DataContract>(output);

If you use this approach you don't need the annotations (all those DataMember and DataContract) on your classes, making code a bit more clean. I very much prefer using this approach than DataContractJsonSerializer, but it's your call.


I've talked a lot about serializing and deserializing objects, but your question was, "How do I find a certain node?". All the discussion above was just a prerequisite.

There are, again and as usual, a few ways of achieving what you want:

  1. @YouneS answer. It's very straightforward and achieves what you are looking for.

  2. Use the second approach above, and then use your typed object to get what you want. For instance:

    var contract = JsonConvert.DeserializeObject<DataContract>(output);
    var query = from dataItem in contract.Response.Data
                where dataItem.Start > 1.3
                order by dataItem.Start;
    var item = query.FirstOrNull();
    

Which will return the first item which, since it's ordered, should be the smallest. Remember to test the result for null.

  1. You can use a feature from Newtonsoft that enables to directly find the node you want. Refer to the documentation. A warning, it's a bit advanced and probably overkill for simple cases.
Bruno Brant
  • 7,558
  • 5
  • 40
  • 76
1

You can make it work with something like the following code :

// Dynamic object that will hold your Deserialized json string    
dynamic myObj = JsonConvert.DeserializeObject<dynamic>(YOUR-JSON-STRING);

// Will hold the value you are looking for
string[] mySubjectValue = "";

// Looking for your subject value    
foreach(var o in myObj.response.data) {
    if(o.start > 1.3)
        mySubjectValue = o.subjects;
}
YouneS
  • 391
  • 4
  • 9