0

I am successfully able to retrieve the JSON body from a URL in Visual Studio 2019 using Newtonsoft library. However I'm having difficulty in parsing out the only variable I am interested in.

I've tried to follow some guides on here that I don't seem to have either implemented correctly or haven't fully understood (I'm pretty new to this). e.g. Unexpected character encountered while parsing API response

RAW JSON:

{ 
"deviceId":37,
"longMacAddress":"5884e40000000439",
"shortMacAddress":259,
"hoplist":"(259,3)",
"associationTime":"2019-06-10 22:43:54",
"lifeCheckInterval":5,
"lastLiveCheck":"2019-06-11 07:11:37",
"onlineStatus":1,
"txPowerValue":14,
"deviceType":1,
"frequencyBand":1,
"lastLivecheck":"2019-06-11 07:11:36",
"disassociationState":0,
"firmwareUpdateActivated":0,
"firmwareUpdatePackagesSent":0,
"firmwareUpdatePackagesUnsent":0,
"firmwareUpdateInProgress":0,
"deviceIdOem":"-1",
"deviceNameOem":"-1",
"deviceCompanyOem":"-1",
"binaryInputCount":0,
"binaryOutputCount":0,
"analogInputCount":0,
"characterStringCount":1,
"location":[
  {
    "location":"UK",
    "locationWriteable":1,
    "changedAt":"2019-06-10 23:40:50"
   }
],
"description": [
  {
    "description":"DevKit",
    "descriptionWriteable":1,
    "changedAt":"2019-06-10 23:40:54"
  }
],
"binaryInput":[],
"binaryOutput":[],
"analogInput":[],
"characterString": [
 {
   "characterString":"149+0.0+99+26.5+0",
   "characterStringWriteable":1,
   "changedAt":"2019-06-11 06:45:02"
  }
]
}

MY MAIN CODE:

using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;

namespace API_JSON_1
{
    class Program
    {
        static void Main(string[] args)
        {
            var client = new WebClient();
            var JSON = client.DownloadString("http://192.168.0.254:8000/nodes/longmac/5884e40000000439");
            Console.WriteLine(JSON);
            Console.WriteLine("----------------------------------------------");
            CharacterString CSV = JsonConvert.DeserializeObject<CharacterString>(JSON);
            Console.WriteLine("Sensor data: " + CSV.CharacterStringCharacterString);
        }
    }
}

MY CHARACTER STRING CLASS:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace API_JSON_1
{
    public class CharacterStrings
    {
        public CharacterStrings CharString { get; set; }
    }

    public class CharacterString
    {   public string CharacterStringCharacterString { get; set; }
        public long CharacterStringWriteable { get; set; }
        public DateTimeOffset ChangedAt { get; set; }
    }
}

OUTPUT TO THE CONSOLE:

{"deviceId":37,"longMacAddress":"5884e40000000439","shortMacAddress":259,"hoplis
t":"(259,3)","associationTime":"2019-06-10 22:43:54","lifeCheckInterval":5,"last
LiveCheck":"2019-06-11 06:56:37","onlineStatus":1,"txPowerValue":14,"deviceType"
:1,"frequencyBand":1,"lastLivecheck":"2019-06-11 06:56:33","disassociationState"
:0,"firmwareUpdateActivated":0,"firmwareUpdatePackagesSent":0,"firmwareUpdatePac
kagesUnsent":0,"firmwareUpdateInProgress":0,"deviceIdOem":"-1","deviceNameOem":"
-1","deviceCompanyOem":"-1","binaryInputCount":0,"binaryOutputCount":0,"analogIn
putCount":0,"characterStringCount":1,"location":[{"location":"UK","locati
onWriteable":1,"changedAt":"2019-06-10 23:40:50"}],"description":[{"description"
:"DevKit","descriptionWriteable":1,"changedAt":"2019-06-10 23:40:54"}],"binaryIn
put":[],"binaryOutput":[],"analogInput":[],"characterString":[{"characterString"
:"149+0.0+99+26.5+0","characterStringWriteable":1,"changedAt":"2019-06-11 06:45:
02"}]}
----------------------------------------------
Sensor data:
Press any key to continue . . .

Obviously I was expecting/hoping that the penultimate line there would read: "Sensor data: 149+0.0+99+26.5+0"

0xNIC
  • 135
  • 2
  • 10
23deano
  • 21
  • 4
  • 1
    Your `CharacterStrings` class has a property that is the same class (`CharacterStrings`), assuming that should be `CharacterString`. Also CharacterString is an array not a single item. – hawkstrider Aug 27 '19 at 15:02
  • Stick your JSON in here https://app.quicktype.io/?l=csharp. It will automatically make the model exactly as you need it. – Andres Castro Aug 27 '19 at 15:13
  • Visual Studio has had the ability to create the JSON classes needed for about 10 years now. Also, there is a difference between *parsing* (the title and body) and deserializing (the code) – Ňɏssa Pøngjǣrdenlarp Aug 27 '19 at 15:32

4 Answers4

1

In order for this to work you have to modify your classes like this:

public class CharacterStrings
{
    public List<CharacterStringObject> CharacterString { get; set; }
}
public class CharacterStringObject
{
    public string CharacterString { get; set; }
    public long CharacterStringWriteable { get; set; }
    public DateTime ChangedAt { get; set; }
}

After that, after you read your JSON like that:

CharacterString CSV = JsonConvert.DeserializeObject<CharacterString>(JSON);

enter image description here

You end up with List<CharacterStringObject> with one element in it. So you take the first one like that and print it:

Console.WriteLine("Sensor data: " + CSV.CharacterString.First().CharacterString);

Cheers,
Edit: Tested it locally with your given JSON, works fine

G.Dimov
  • 2,173
  • 3
  • 15
  • 40
0

The CharacterString property in the json is an array of objects. Also the first property in those objects is characterString. Below is a working example

Output is Sensor Data: 149+0.0+99+26.5+0

using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;

namespace SOTest
{
    class Program
    {
        static void Main(string[] args)
        {
            var json =
                "{\"deviceId\":37,\"longMacAddress\":\"5884e40000000439\",\"shortMacAddress\":259,\"hoplist\":\"(259,3)\",\"associationTime\":\"2019-06-10 22:43:54\",\"lifeCheckInterval\":5,\"lastLiveCheck\":\"2019-06-11 07:11:37\",\"onlineStatus\":1,\"txPowerValue\":14,\"deviceType\":1,\"frequencyBand\":1,\"lastLivecheck\":\"2019-06-11 07:11:36\",\"disassociationState\":0,\"firmwareUpdateActivated\":0,\"firmwareUpdatePackagesSent\":0,\"firmwareUpdatePackagesUnsent\":0,\"firmwareUpdateInProgress\":0,\"deviceIdOem\":\"-1\",\"deviceNameOem\":\"-1\",\"deviceCompanyOem\":\"-1\",\"binaryInputCount\":0,\"binaryOutputCount\":0,\"analogInputCount\":0,\"characterStringCount\":1,\"location\":[{\"location\":\"UK\",\"locationWriteable\":1,\"changedAt\":\"2019-06-10 23:40:50\"}],\"description\":[{\"description\":\"DevKit\",\"descriptionWriteable\":1,\"changedAt\":\"2019-06-10 23:40:54\"}],\"binaryInput\":[],\"binaryOutput\":[],\"analogInput\":[],\"characterString\":[{\"characterString\":\"149+0.0+99+26.5+0\",\"characterStringWriteable\":1,\"changedAt\":\"2019-06-11 06:45:02\"}]}";

            var obj = JsonConvert.DeserializeObject<CharacterStrings>(json);
            Console.WriteLine($"Sensor Data: {obj.CharacterString.First().CharacterString}");
            Console.ReadKey();
        }

        public class CharacterStrings
        {
            public List<Characters> CharacterString { get; set; }
        }

        public class Characters
        {   public string CharacterString { get; set; }
            public long CharacterStringWriteable { get; set; }
            public DateTime ChangedAt { get; set; }
        }
    }
}
hawkstrider
  • 4,141
  • 16
  • 27
  • If I change the ```public class CharacterStrings``` as you suggest I get an error on the line: ```public string Character String { get; set; }``` which reads ```'CharacterString': member names cannot be the same as their enclosing type``` – 23deano Aug 27 '19 at 15:14
  • I corrected that because you can't have a property with the same name as the class. The above example is working – hawkstrider Aug 27 '19 at 15:14
0

You are defining the CSV object with CharacterSting. I should be CharacterStrings and Inside the CharacterStrings class it should to be array because your JSON is an array

0

Welcome to StackOverflow!

Something you need to keep in mind when converting a json string to an object (ie: Deserialize) is that the object to which you are converting the string into needs to match the format of the json string. In your case, the json string does not match the object to which you are converting.

Once you have the correct object, this will give you the output you want:

    static void Main(string[] args)
    {
        string JSON = "{\"deviceId\":37,\"longMacAddress\":\"5884e40000000439\",\"shortMacAddress\":259,\"hoplist\":\"(259,3)\",\"associationTime\":\"2019-06-10 22:43:54\",\"lifeCheckInterval\":5,\"lastLiveCheck\":\"2019-06-11 07:11:37\",\"onlineStatus\":1,\"txPowerValue\":14,\"deviceType\":1,\"frequencyBand\":1,\"lastLivecheck\":\"2019-06-11 07:11:36\",\"disassociationState\":0,\"firmwareUpdateActivated\":0,\"firmwareUpdatePackagesSent\":0,\"firmwareUpdatePackagesUnsent\":0,\"firmwareUpdateInProgress\":0,\"deviceIdOem\":\"-1\",\"deviceNameOem\":\"-1\",\"deviceCompanyOem\":\"-1\",\"binaryInputCount\":0,\"binaryOutputCount\":0,\"analogInputCount\":0,\"characterStringCount\":1,\"location\":[{\"location\":\"UK\",\"locationWriteable\":1,\"changedAt\":\"2019-06-10 23:40:50\"}],\"description\":[{\"description\":\"DevKit\",\"descriptionWriteable\":1,\"changedAt\":\"2019-06-10 23:40:54\"}],\"binaryInput\":[],\"binaryOutput\":[],\"analogInput\":[],\"characterString\":[{\"characterString\":\"149+0.0+99+26.5+0\",\"characterStringWriteable\":1,\"changedAt\":\"2019-06-11 06:45:02\"}]}";
        Console.WriteLine(JSON);
        Console.WriteLine("----------------------------------------------");
        RootObject CSV = JsonConvert.DeserializeObject<RootObject>(JSON);

        // Option 1: Loop over the items in your List<CharacterString>...

        foreach (var cs in CSV.characterString)
        {
            Console.WriteLine("Sensor data: " + cs.characterString);
        }

        // Option 2: Or, if you know there is only one in the list...

        Console.WriteLine("Sensor data: " + CSV.characterString.First().characterString);
   }

EDIT to explain the code above: Because characterString is a List<CharacterString>, it's technically possible that you could have more than one item in that list. Option 1: If you want, you can loop through that list to display the items in it. But if you know for certain that there will only be one item in the list, then, Option 2: You can just display the .First() item in the list.

I took the json sting you gave us, and using this handy website (http://json2csharp.com/#) I converted it into the following classes:

public class RootObject
{
    public int deviceId { get; set; }
    public string longMacAddress { get; set; }
    public int shortMacAddress { get; set; }
    public string hoplist { get; set; }
    public string associationTime { get; set; }
    public int lifeCheckInterval { get; set; }
    public string lastLiveCheck { get; set; }
    public int onlineStatus { get; set; }
    public int txPowerValue { get; set; }
    public int deviceType { get; set; }
    public int frequencyBand { get; set; }
    public string lastLivecheck { get; set; }
    public int disassociationState { get; set; }
    public int firmwareUpdateActivated { get; set; }
    public int firmwareUpdatePackagesSent { get; set; }
    public int firmwareUpdatePackagesUnsent { get; set; }
    public int firmwareUpdateInProgress { get; set; }
    public string deviceIdOem { get; set; }
    public string deviceNameOem { get; set; }
    public string deviceCompanyOem { get; set; }
    public int binaryInputCount { get; set; }
    public int binaryOutputCount { get; set; }
    public int analogInputCount { get; set; }
    public int characterStringCount { get; set; }
    public List<Location> location { get; set; }
    public List<Description> description { get; set; }
    public List<object> binaryInput { get; set; }
    public List<object> binaryOutput { get; set; }
    public List<object> analogInput { get; set; }
    public List<CharacterString> characterString { get; set; }
}

public class Location
{
    public string location { get; set; }
    public int locationWriteable { get; set; }
    public string changedAt { get; set; }
}

public class Description
{
    public string description { get; set; }
    public int descriptionWriteable { get; set; }
    public string changedAt { get; set; }
}

public class CharacterString
{
    public string characterString { get; set; }
    public int characterStringWriteable { get; set; }
    public string changedAt { get; set; }
}
Casey Crookston
  • 13,016
  • 24
  • 107
  • 193
  • Thanks for the advice, I will give it a shot. Can you explain what the following lines do? I think its two methods of achieving the same outcome: ```foreach (var cs in CSV.characterString) { Console.WriteLine("Sensor data: " + cs.characterString); } // or Console.WriteLine("Sensor data: " + CSV.characterString.First().characterString);``` – 23deano Aug 28 '19 at 07:55
  • @23deano, yes, you are correct :) Assuming there is only one item in the list, then both of these will do the same thing. If, however, there are multiple items in the list, then with the second option, only the first item in the list will be displayed and all the others will be ignored. Because `CSV.characterString` is a `List`, it could potentialy have multiple items in the list. But when you call `.First()` on it, you are asking for the first, and only the first, item. I also edited my answer to explain better. – Casey Crookston Aug 28 '19 at 13:12
  • Thanks again! I've been able to implement the changes you suggested and I can now get the output I need, live from the application. I need to get my head around how the deserialisation works. It takes ```RootObject```, which is all of the raw JSON laid out into its separate classes and puts it in a variable ```CSV```. From there we can write to the console the first (or second, or third etc...) element of the ```characterString``` array. (I think in my first attempt I also had too many objects, arrays and elements called too similar names, which didn't help). – 23deano Aug 28 '19 at 15:05
  • Deserialization and Serialisation are really simple. In the case of JSON, they just take a string and convert it into an object, or vice-versa. In your case, `RootObject` (and you can change that name if you want) is just a class that has all the same properties as your json string. So mapping between the two is simple. Newtonsoft makes it super easy. Take a json string and convert it (ie: Deserialize it) into an instance of `RootObject'. C# can't really do much with a string. But it can do a LOT with `RootObject`. – Casey Crookston Aug 28 '19 at 15:10