How to deserailise JSON Object in C# Without knowing structure - c#

Is it possible to take a JSON object in C# that I read from from another source and convert the contents to files. The problem is I don't know the structure of the incoming objects.
So far I've got this:
var response = await client.GetAsync("https://blahblah/creds" +
"?ApiKey=" + textBox_password.Text +
"&deviceId=" + deviceId +
"&modelNum=" + modelNum);
var res = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
var resJson = JsonConvert.DeserializeObject(res);
this.textBox_Stream.AppendText("responseBody = " + resJson + "\r\n");
Which gives me:
responseBody = {
"statusCode": 200,
"body": {
"modelNum": "42",
"creds": "[{\"deviceid.txt\":\"4DA23E\",\"pac.txt\":\"580795498743\"}]",
"deviceId": "4DA23E"
}
}
What I want to do is create a folder called 4DA23E and place one file inside it for each entry in the creds object.
device.txt will contain 4DA23E
pac.tct will contain 580795498743
etc. I can't figure out a dynamic way to extract the stuff I need. Goes without saying, I am not a C# programmer! So, please be kind.

Don't use JsonConvert.DeserializeObject. That's best when you know the schema of the JSON object and you've made a corresponding C# model.
First, parse the outer response JSON
var res = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
JObject resJson = JObject.Parse(res);
Now pull out the value of the "creds" property and re-parse it.
var credsJsonRaw = resJson["body"]["creds"].Value<string>();
JArray credsJson = JArray.Parse(credsJsonRaw);
Now you can loop through each object in the array, and each property in each object.
foreach (JObject obj in credsJson)
{
foreach (JProperty prop in obj.Properties)
{
Console.WriteLine($"Name = {prop.Name}");
Console.WriteLine($"Value = {prop.Value.Value<string>()}");
}
}
For your example, this prints
Name = deviceid.txt
Value = 4DA23E
Name = pac.txt
Value = 580795498743

One way to deserialise the following json is in two stages. This is because 'creds' is a string.
{
"statusCode": 200,
"body": {
"modelNum": "42",
"creds": "[{\"deviceid.txt\":\"4DA23E\",\"pac.txt\":\"580795498743\"}]",
"deviceId": "4DA23E"
}
}
Here's an example of how to do it. If you're not .NET6 change 'record' to 'class' and if you don't use nullable reference types (on by default in .NET6) remove the appropriate question marks in property definitions.
using System.Text.Json;
using System.Text.Json.Serialization;
var json = File.ReadAllText("data.json");
var o = JsonSerializer.Deserialize<Outer>(json);
if(o?.Body?.Creds == null)
{
throw new Exception("Bad data");
}
var credList = JsonSerializer.Deserialize<List<Dictionary<string, string>>>(o.Body.Creds);
if(credList == null)
{
throw new Exception("Bad data");
}
foreach( var e in credList)
foreach( var kvp in e)
Console.WriteLine($"{kvp.Key} : {kvp.Value}");
record Outer {
[JsonPropertyName("staticCode")]
public int? StaticCode {get; set;}
[JsonPropertyName("body")]
public Body? Body {get; set;}
}
record Body {
[JsonPropertyName("creds")]
public string? Creds {get; set;}
}
This prints
deviceid.txt : 4DA23E
pac.txt : 580795498743

Related

Parsing JSON response from StreamBuilder C#

I'm getting a JSON response back from another website and then building the response from a StreamReader:-
using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
{
response = streamReader.ReadToEnd();
}
The result I'm getting is:-
string response = "{\"d\":\"[{\\\"Animal\\\":\\\"Cat\\\",\\\"Noise\\\":\\\"Meow\\\"},{\\\"Animal\\\":\\\"Dog\\\",\\\"Noise\\\":\\\"Woof\\\"}]\"}";
I've then used the JsonConvert.DeserializeObject(response) to deserialise and then I'm then trying to loop through the results to read the values.. but it's not working whatever I try
dynamic jObj = JsonConvert.DeserializeObject(response);
var arr = jObj; //Tried var arr = jObj.d;
#foreach (var item in arr)
{
….
}
Error: Target type System.Collections.IEnumerable is not a value type or a non-abstract class.
Parameter name: targetType
Your JSON response contains a d property, which value is an array wrapped into string itself.
So, you should parse a d content separately into array, Json.Linq solution is below
string response = "{\"d\":\"[{\\\"Animal\\\":\\\"Cat\\\",\\\"Noise\\\":\\\"Meow\\\"},{\\\"Animal\\\":\\\"Dog\\\",\\\"Noise\\\":\\\"Woof\\\"}]\"}";
var json = JObject.Parse(response);
var array = json["d"];
foreach (var item in JArray.Parse(array.ToString()))
{
Console.WriteLine(item["Animal"]);
}
Solution with deserialization to dynamic object
dynamic json = JsonConvert.DeserializeObject(response);
var array = json?.d;
foreach (var item in JsonConvert.DeserializeObject(array?.ToString()))
{
Console.WriteLine(item?.Animal);
}
It allows you to parse the response without any changes to source JSON
If you printed response, you would see that jObj.d is not an array, but a string. In fact, it looks like the string representation of an array of objects.
It seems that the "d"'s value contains another JSON data. If it is correct and you don't want to touch the JSON format, here is the solution:
string response = "{\"d\":\"[{\\\"Animal\\\":\\\"Cat\\\",\\\"Noise\\\":\\\"Meow\\\"},{\\\"Animal\\\":\\\"Dog\\\",\\\"Noise\\\":\\\"Woof\\\"}]\"}";
dynamic jObj = JsonConvert.DeserializeObject(response);
var arr = JsonConvert.DeserializeObject<D[]>((jObj.d).Value);
foreach (var item in arr)
{
}
........
public class D
{
public string Animal { get; set; }
public string Noise { get; set; }
}

C# Complex class construct Read/Set Value from JSON

I have a problem; I have various (too many) classes, that are linked as Parent/Child
I populate initially my top class while instantiate the others, as follows (works):
TopClass MyClass = new TopClass()
{
Headers = new someHeaders()
{
CorrID = "1234567890",
RequestID = "1234567890",
L_Token = "abcdefghijklmnopqrstuvwxyz"
},
Body = new someBody()
{
access = new AccessHeaders()
{
allPs = "allAcc",
availableAcc = "all"
},
CombinedServiceIndicator = false,
FrequencyPerDay = 10,
ValidUntil = "2020-12-31"
},
ResponseAttributes = new ConsentResponse()
{
myId = String.Empty,
myStatus = String.Empty,
myName = String.Empty,
_links_self_href = String.Empty,
status_href = String.Empty
}
};
The initials values that I populate above rarely change, but the Classes' properties change as the project goes on, and each class ends up having many properties.
I need to parse the properties and set values to any properties that match their name, but I can't seem to figure out why despite following official examples.
(I read the input data from a JSON string I use Newtonsoft - have tried everything out there)
I can't find a way to deserialize the JSON and assign any values to my properties without using the name of the property, i.e. without saying
MyClass.Body.access.allPs = "Hello"
but something like
var json = response.Content.ReadAsStringAsync().Result.ToString();
var a = new { serverTime = "", data = new object[] { } };
var c = new JsonSerializer();
or
if (myresponse.Attributes.GetType().GetTypeInfo().GetDeclaredProperty(key) != null)
myresponse.GetType().GetTypeInfo().GetDeclaredProperty(key).SetValue(myresponse, entry.Value);
//and how do I read the value, parse my TopClass and assign the value to correct property?
Could someone help me?
Values for the properties will get automatically assigned if the JSON is having the correct structure as per the class mentioned above.
Something like:
{
"Body":
"access:
{
"allPs" = "required value"
}
}
Then you can use:
var result = JsonConvert.DeserializeObject < TopClass > (json );

Two almost identical functions, one works, the other does not?

Working on writing a C# app to talk to our ServiceNow API. For some reason I can successfully ignore the root token of the JSON file it returns for the user, but not for the Incident. JSON returns seem to be almost identical save for the fact that one is from the incident table and the other is from the sys_user table.
Tried a couple of different ways of using Newtonsoft in the process of getting to the ignoring of the root token. Which has led me to where this code is at now (albeit unrefined)
void incidentCreator(string incID)
{
var restRequest = new RestRequest(_incTableLink + "/" + incID);
restRequest.AddParameter("sysparm_display_value", "true");
restRequest.AddParameter("sysparm_fields", "assigned_to,number,sys_updated_on,sys_id,comments_and_work_notes");
string incidentJsonString = _restInteractions.querySnow(restRequest);
Incident incident = null;
try
{
incident = JObject.Parse(incidentJsonString).SelectToken("result[0]").ToObject<Incident>();
}
catch (Exception e)
{
Console.WriteLine(e);
}
_incidentList.Add(incident);
}
void userGetter(string userID)
{
var restRequest = new RestRequest(_userTableLink);
restRequest.AddParameter("sysparm_query", "sys_id=" + userID);
string userJsonString = _restInteractions.querySnow(restRequest);
User user = null;
try
{
user = JObject.Parse(userJsonString).SelectToken("result[0]").ToObject<User>();
}
catch (Exception e)
{
Console.WriteLine(e);
}
//User user = JsonConvert.DeserializeObject<User>(userJsonString);
_userList.Add(user);
}
JSON string for incident that doesn't work
"{
\"result\": {
\"number\": \"INC1215653\",
\"sys_id\": \"52764be80007fb00903d51fea4ade50f\",
\"comments_and_work_notes\": \"2019-08-01 10: 46: 19 - PERSON (Additional comments)\\nEmailed PERSON
admin of the site to check permissions\\n",
\"sys_updated_on\": \"2019-08-01 10: 46: 17\",
\"assigned_to\": \"Employee\"
}
}"
JSON string for user which does work
"{
\"result\": [
{
\"calendar_integration\": \"1\",
\"last_position_update\": \"\",
\"u_lmc_account_indicator\": \"\",
\"u_supply_chain\": \"false\",
}
]
}"
No matter what I do with the incidentCreator() JSON parsing, using jsonconvert or the implementation present in current shared code. I either get "Object reference not set to an instance of an object." or just an object with all it's variables being null.
Any ideas? I'm sorry for the stripped back user json, it is a huge string and i couldn't be bothered cleaning it of PII before posting.
The JPath expression in your incidentCreator is incorrect.
It should be:-
try { incident = JObject.Parse(incidentJsonString).SelectToken("result").ToObject<Incident>(); }
Also, It's possibly just a result of pasting the JSON into the question, but the "comments_and_work_notes" field of the incident JSON example isn't escaped properly; just check you're receiving valid JSON using a linter to be sure.
The second JSON contents an array. You can see the différences [ (array) and { (object) after the word result
To parse for the case user, try it
JArray a = JArray.Parse(json);
var listUser = new List<User>();
foreach (JObject o in a.Children<JObject>())
{
foreach (JProperty p in o.Properties())
{
string name = p.Name;
string value = (string)p.Value;
var user = new User();
user.Name = name;
......
}
}
The difference is that your JSON string for incident has the field result which stores an object and the JSON string for user has the field result which contains an array of one object
incident
{
result :
{
...
}
}
user
{
result :
[
{
...
}
]
}
so the query SelectToken("result[0]") will work for the user since the result field contains an array with an element at index 0, but it wont work for the incident since there the result field does not conatin an array.
But SelectToken("result").ToObject<Incident>() should give you your Incident object.

Additional text encountered after JSON reading

I have this JSON:
{"id":1,"name":"Alabama"}
{"id":2,"name":"Alaska"}
{"id":3,"name":"Arizona"}
{"id":4,"name":"Arkansas"}
and this code:
string[] testDataFiles = new string[] { StatesOneFileName, StatesTwoFileName, ContinentsFileName };
foreach (var testFile in testDataFiles)
{
using (StreamReader r = new StreamReader(testFile))
{
string json = r.ReadToEnd();
dynamic jsonObjectArray = JsonConvert.DeserializeObject(json);
foreach (var item in jsonObjectArray)
{
expectedPartitions.Add(item.name);
}
}
}
When I run this, however, I get this error:
Additional text encountered after finished reading JSON content: {. Path '', line 2, position 0.
I've read a few StackOverflow suggestions to encase the test data in [], but unfortunately I cannot edit the json file itself. Is there a way to wrap something in my code such that it reads the correct way? Thanks.
Assuming a file that contains a json object per line, as follows:
{"id":1,"name":"Alabama"}
{"id":2,"name":"Alaska"}
{"id":3,"name":"Arizona"}
{"id":4,"name":"Arkansas"}
You can create the following model for your data:
public class State
{
[JsonProperty("id")]
public int Id { get; set; }
[JsonProperty("name")]
public string Name{ get; set; }
}
And then create a simple method that reads the file line by line, as follows:
public static IEnumerable<State> GetStates(string path)
{
if (string.IsNullOrWhiteSpace(path))
{
throw new ArgumentException("Path to json file cannot be null or whitespace.", nameof(path));
}
if (!File.Exists(path))
{
throw new FileNotFoundException("Could not find json file to parse!", path);
}
foreach (string line in File.ReadLines(path).Where(x => !string.IsNullOrWhiteSpace(x)))
{
State state = JsonConvert.DeserializeObject<State>(line);
yield return state;
}
}
Using the code:
string path = ".....";
// get the list of State objects...
List<State> states = GetStates(path).ToList();
// or just the list of state names
List<string> stateNames = GetStates(path).Select(x => x.Name).ToList();
If this is your json file, it's not correctly formatted... You have multiple json objects in it, but it's neither enclosed in [] to show it's an array of json objects, nor are they delimited by comma.
The best thing you can do here, is to read line at a time, and convert it to json object, since it seems that each line represents one json object.
string json = "start";
using (StreamReader r = new StreamReader(testFile)) {
while (!String.IsNullOrWhiteSpace(json)){
json = r.ReadLine();
dynamic jsonObject = JsonConvert.DeserializeObject(json);
//DO YOUR MAGIC HERE
}
}
Here's one way you could easily fix the syntax errors in the json from that file, without modifying your existing code too much:
foreach (var testFile in testDataFiles)
{
// read the contents of the file with invalid json format, and build a new string named cleanJson with the fixed file contents
string[] fileLines = File.ReadAllLines(testFile);
string cleanJson = "["; // add an opening array bracket to make it valid json syntax
for (int i = 0; i < fileLines.Length; i++)
{
cleanJson += fileLines[i];
if (i < fileLines.Length - 1)
cleanJson += ","; // add a comma to the end of each line (except the last) to make it valid json syntax
}
cleanJson += "]"; // add a closing array bracket to make it valid json syntax
dynamic jsonObjectArray = JsonConvert.DeserializeObject(cleanJson);
foreach (var item in jsonObjectArray)
{
expectedPartitions.Add(item.name);
}
}

Jquery Post JsonString to c#. How do I parse it

I have a Json here and post it to the server.
newJson.push({ Klasse_Id: parseInt(key), MaxTeilnehmer: value.MaxTeilnehmer });
$http.post('#Url.Content("~/Gw2RaidCalender/SaveEvent")', {
model: saveEventObject,
klassenModel: JSON.stringify(newJson)
}).then(function successCallback(result) {
console.log(result.data);
}),
function errorCallback(result) {
console.log(result.data);
};
Now in my C# code i have this
public string SaveEvent(EventModel model, string klassenModel)
{
JObject result = JObject.Parse(klassenModel);
Now i get this Error
Error reading JObject from JsonReader. Current JsonReader item is not an object: StartArray.
It looks like its not an valid Json string !?
"[{\"Klasse_Id\":1,\"MaxTeilnehmer\":2}]"
But I checked it on https://jsonlint.com/ and it says its valid.
I want to get the values out of this Json.
EDIT:
I solved it and now I can save the data
var jsonKlassen = JArray.Parse(klassenModel);
foreach (JObject content in jsonKlassen.Children<JObject>())
{
int klasse_Id = (int)content.Property("Klasse_Id").Value;
int maxTeilnehmer = (int)content.Property("MaxTeilnehmer").Value;
var klassenmodel = new Klasse2EventModel
{
Klasse_Id = klasse_Id,
MaxTeilnehmer = maxTeilnehmer,
Event_Id = newEventId
};
_db.Klasse2EventModel.Add(klassenmodel);
_db.SaveChanges();
}
Because this JSON string contains an array. Try this:
var result = JArray.Parse(klassenModel);

Categories

Resources