This is my first try to deserialize a JSON string returned by Facebook.
I want to make sure future developers can maintain my code comfortably so I would like to know if this is okay way to do it. I am concerned that if the JSON string change then I will need to rewrite some of my classes.
Is the JSON string returned from Facebook likely to change? For example if location became a different object I will need to make changes right?
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using System.Web;
//--- If you are not able to add reference dont worry. You probably need to install ASP.NET Ajax or target higher .NET Framework (3.5 or 4).
//--- There are Stack Overflow threads about this.
using System.Web.Script.Serialization;
//--- Our Facebook Person class.
public class FBPerson
{
public string id { get; set; }
public string email { get; set; }
public string name { get; set; }
public string gender { get; set; }
public IDName location { get; set; }
public List<FBObject> work { get; set; }
public List<FBObject> education { get; set; }
}
//-- In some cases only id and name will be accessed
public class IDName
{
public string id { get; set; }
public string name { get; set; }
public string type { get; set; }
}
//-- work and education presently are array of json strings
public class FBObject
{
public IDName employer { get; set; }
public IDName school { get; set; }
}
static class Program
{
//-- Sample JSON string returned by Facebook
const string json2 = #"{
""id"":""11111111111111111"",
""name"":""Tester Test"",
""first_name"":""Tester"",
""last_name"":""Test"",
""link"":""http:\/\/www.facebook.com\/profile.php?id=11111111111111111"",
""location"":
{""id"":""107991659233606"",""name"":""Atlanta, Georgia""},
""work"":
[{""employer"":{""id"":""222222222222222222"",""name"":""Various""}}],
""education"":
[
{""school"":{""id"":""108000000000000"",""name"":""Test High School""},""type"":""High School""},
{""school"":{""id"":""105000000000000"",""name"":""Tester College""},""type"":""College""}
],
""gender"":""male"",
""email"":""tester\u0040gmail.com"",
""timezone"":-5,""locale"":""en_US"",
""verified"":true,
""updated_time"":""2011-11-21T21:10:20+0000""
}";
static void Main()
{
JavaScriptSerializer ser = new JavaScriptSerializer();
FBPerson fperson = ser.Deserialize<FBPerson>(json2);
//-- Display the user info from the JSON string
Console.WriteLine(fperson.id.ToString());
Console.WriteLine(fperson.name.ToString());
Console.WriteLine(fperson.gender.ToString());
Console.WriteLine(fperson.email.ToString());
Console.WriteLine(fperson.location.name.ToString());
Console.WriteLine(fperson.work[0].employer.name.ToString());
Console.WriteLine(fperson.education[0].school.name.ToString());
Console.ReadLine();
}
}
This seems fine. No matter how you do deserialization, changes to the JSON data format would break you. For that reason, API authors try very hard to avoid making such changes.
I don't know whether you are open to other approaches but in case you may want to try,
here is an alternative way that doesn't need FBPerson IDName FBObject classes and makes use of Json.Net and this extension methods
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
static class Program
{
//-- Sample JSON string returned by Facebook
const string json2 = #"{
""id"":""11111111111111111"",
""name"":""Tester Test"",
""first_name"":""Tester"",
""last_name"":""Test"",
""link"":""http:\/\/www.facebook.com\/profile.php?id=11111111111111111"",
""location"":
{""id"":""107991659233606"",""name"":""Atlanta, Georgia""},
""work"":
[{""employer"":{""id"":""222222222222222222"",""name"":""Various""}}],
""education"":
[
{""school"":{""id"":""108000000000000"",""name"":""Test High School""},""type"":""High School""},
{""school"":{""id"":""105000000000000"",""name"":""Tester College""},""type"":""College""}
],
""gender"":""male"",
""email"":""tester\u0040gmail.com"",
""timezone"":-5,""locale"":""en_US"",
""verified"":true,
""updated_time"":""2011-11-21T21:10:20+0000""
}";
public static void Main()
{
dynamic fperson = JsonUtils.JsonObject.GetDynamicJsonObject(json2);
//-- Display the user info from the JSON string
Console.WriteLine(fperson.id.ToString());
Console.WriteLine(fperson.name.ToString());
Console.WriteLine(fperson.gender.ToString());
Console.WriteLine(fperson.email.ToString());
Console.WriteLine(fperson.location.name.ToString());
Console.WriteLine(fperson.work[0].employer.name.ToString());
Console.WriteLine(fperson.education[0].school.name.ToString());
Console.ReadLine();
}
}
Related
This question already has answers here:
Parse JSON response where the object starts with a number in c#
(2 answers)
Closed 1 year ago.
I am trying to get the List from a JSON string from a 3rd party API.
I am not able to understand how should I parse it,as the key name starts with numeric.
I do not need the key but I do require value, but I require the value part in my code to be stored in DB.
JSON
{
"5MIN": [
{
"SETTLEMENTDATE": "2021-08-16T00:30:00",
"REGIONID": "NSW1",
"REGION": "NSW1",
"RRP": 39.27,
"TOTALDEMAND": 7416.02,
"PERIODTYPE": "ACTUAL",
"NETINTERCHANGE": -788.69,
"SCHEDULEDGENERATION": 5518.17,
"SEMISCHEDULEDGENERATION": 1076.47
},
{
"SETTLEMENTDATE": "2021-08-16T01:00:00",
"REGIONID": "NSW1",
"REGION": "NSW1",
"RRP": 36.51,
"TOTALDEMAND": 7288.89,
"PERIODTYPE": "ACTUAL",
"NETINTERCHANGE": -828.1,
"SCHEDULEDGENERATION": 5362.3,
"SEMISCHEDULEDGENERATION": 1064.35
}
]
}
I think I am over complicating the issue, but I am confused
As mentioned in the comments above, you can paste the json as code.
Next you add a reference to Newtonsoft.Json:
dotnet add package newtonsoft.json
You then call JsonConvert.Deserialize<T>() as in the example below:
using System;
using System.Collections.Generic;
using System.IO;
using Newtonsoft.Json;
using StackOverflow;
using System.Linq;
//In this example I load the JSON from disk
var json = File.ReadAllText("/home/timothy/data.json");
var record = JsonConvert.DeserializeObject<ServiceResponse>(json);
//No need to convert to List<T> if you're not going to filter it
var results = record.The5Min.ToList();
foreach(var item in results)
{
Console.WriteLine($"{item.Settlementdate}, {item.Regionid}");
}
namespace StackOverflow
{
using System;
using System.Collections.Generic;
using System.Globalization;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
public partial class ServiceResponse
{
[JsonProperty("5MIN")]
public The5Min[] The5Min { get; set; }
}
public partial class The5Min
{
[JsonProperty("SETTLEMENTDATE")]
public DateTimeOffset Settlementdate { get; set; }
[JsonProperty("REGIONID")]
public string Regionid { get; set; }
[JsonProperty("REGION")]
public string Region { get; set; }
[JsonProperty("RRP")]
public double Rrp { get; set; }
[JsonProperty("TOTALDEMAND")]
public double Totaldemand { get; set; }
[JsonProperty("PERIODTYPE")]
public string Periodtype { get; set; }
[JsonProperty("NETINTERCHANGE")]
public double Netinterchange { get; set; }
[JsonProperty("SCHEDULEDGENERATION")]
public double Scheduledgeneration { get; set; }
[JsonProperty("SEMISCHEDULEDGENERATION")]
public double Semischeduledgeneration { get; set; }
}
}
At the beginning I want to say that 'm not a C# developer by any means, however at my work I've received a task where I have to write simple web service in .net.
The task is not very complicated however I've encountered the problem that JSON payload which is sent to our web service has "System" in property names:
"resource": {
"fields": {
"System.AreaPath": "someData",
"System.TeamProject": "someData",
"System.IterationPath": "someData"
}
}
I'm trying to get those values by using:
public class Resource
{
public Fields System.AreaPath { get; set; }
}
However I'm getting an error ("System" is a namespace but used like a type.)
Are there any best practices on how to perform such task ?
Thank you.
One easy option it to specify the JSON property in an attribute. If you're using Json.NET you can use [JsonProperty] for this. Complete example:
using System;
using System.IO;
using Newtonsoft.Json;
public class Fields
{
[JsonProperty("System.AreaPath")]
public string AreaPath { get; set; }
[JsonProperty("System.TeamProject")]
public string TeamProject { get; set; }
[JsonProperty("System.IterationPath")]
public string IterationPath { get; set; }
}
public class Resource
{
public Fields Fields { get; set; }
}
class Program
{
static void Main()
{
string json = File.ReadAllText("test.json");
var resource = JsonConvert.DeserializeObject<Resource>(json);
Console.WriteLine(resource.Fields.TeamProject);
}
}
JSON (removed the "resource" part to make it a complete JSON document; I assume you know how to handle this if necessary):
{
"fields": {
"System.AreaPath": "someData",
"System.TeamProject": "team project",
"System.IterationPath": "someData"
}
}
Output: team project
I've got some difficulties with this json script:
{
"insured_agent_flag": "a",
"id": "1",
"agent": {
"fullName": "John Travolta",
"mobileNumberPdf": "+987654321",
"mobileNumber": "",
"identityCard": {
"identityCardExpirationDate": null
},
"secondIdentityCard": {
"identityCardExpirationDate": null
},
"notes": {},
"sign": "ADVANCED"
},
"basicData": {
"personType": "PERSON",
"agreeWithCompleteAnalysis": false,
"investmentInterest": false
},
"nonOfferedProducts": [
"PROD_A",
"PROD_B",
"PROD_C"
]
}
I would like to get some parameters from this script and put it into sql server table.
In order to do that, I used and transformed a C# script shared by https://mycontraption.com:
using System;
using System.Data;
using System.Collections.Generic;
using System.Linq;
using Microsoft.SqlServer.Dts.Pipeline.Wrapper;
using Microsoft.SqlServer.Dts.Runtime.Wrapper;
using System.Web.Script.Serialization;
using Microsoft.SqlServer.Dts.Pipeline;
namespace SC_c7e2d8c3918d46a5a07a1b438ddc7642
{
public class BasicData
{
public string agreeWithCompleteAnalysis { get; set; }
public string inOtherSystem { get; set; }
public string investmentInterest { get; set; }
}
public class ParentObject
{
public BasicData BasicData { get; set; }
public int id { get; set; }
public string insured_agent_flag { get; set; }
public IEnumerable<string> NonOfferedProducts { get; set; }
}
[Microsoft.SqlServer.Dts.Pipeline.SSISScriptComponentEntryPointAttribute]
public class ScriptMain : UserComponent
{
public override void Input0_ProcessInputRow(Input0Buffer Row)
{
JavaScriptSerializer js = new JavaScriptSerializer();
// Give the input column a variable to make it easier to reference.
BlobColumn combinedColumn = Row.parameterscon;
// Convert from blob to string
string reviewConverted = System.Text.Encoding.ASCII.GetString(combinedColumn.GetBlobData(0, Convert.ToInt32(combinedColumn.Length)));
// Deserialize the string
ParentObject obj = js.Deserialize<ParentObject>(reviewConverted);
var rows = obj.NonOfferedProducts.ToList();
Row.agreeWithCompleteAnalysis = obj.BasicData.agreeWithCompleteAnalysis;
Row.inOtherSystem = obj.BasicData.inOtherSystem;
Row.investmentInterest = obj.BasicData.investmentInterest;
Row.projectionid = obj.id;
Row.insuredagentflag = obj.insured_agent_flag;
//Row.nonOfferedProducts =
}
}
}
For 'standard' objects it works fine, but there is a problem with array "nonOfferedProducts". After compiling I get an error:
„object reference not set to an instance of an object”.
Here are my questions:
1. How should I handle 'nonOfferedProducts' array in C# script?
2. Why do I get foregoing error?
3. Unfortunately there exists a possibility, that json scripts would have some errors, like missing braces. How should I handle that?
Thank you!
Thanks a lot for your answers. According to your comments I'll try to give you more explanations:
1. The json script I have added in this post - it's only small part of whole script. In complete script there is a lot of different parameters. What is more, my C# code should scan about 40.000 json scripts (stored in sql server table in one column). These scripts has got similiar structure - but not the same.
So I thought about C# resolution, that will be searching for the parameters that I need. For json scripts without these parameters the c# code will put nulls to the right output columns.
Here are my output columns:
-agreeWithCompleteAnalysis
-inOtherSystem
-investmentInterest
-projectionId
-insuredAgentFflag
-nonOfferedProducts
I understood, that structure of my classes were wrong - I'll improve that.
But I've got one doubt - is it possible to prepare c# code structure, that will handle only these parameters I need?
And finally, I would like to put the results into my database.
For example if nonOfferedProducts property will have 3 values (not always!), I'd like to send to my database table 3 records (3 different values for nonOfferedProducts column and 3 the same values for the rest columns -agreeWithCompleteAnalysis, inOtherSystem etc).
I hope that will be clear now.
Thanks a lot for your help!
J
Use https://quicktype.io and paste json, it will generate c# model and serializer code.
As I said in my comment, your c# model doesn't match the JSON object.
If the model was made up of various nested objects to better reflect the actual JSON then you'll have more luck:
public class IdentityCard
{
public DateTime? IdentityCardExpirationDate { get; set; }
}
public class Notes
{
//No idea what should be in here...
}
public class BasicData
{
public string PersonType { get; set; }
public bool AgreeWithCompleteAnalysis { get; set; }
public bool InvestmentInterest { get; set; }
}
public class Agent
{
public string FullName { get; set; }
public string MobileNumberPdf { get; set; }
public string MobileNumber { get; set; }
public IdentityCard IdentityCard { get; set; }
public IdentityCard SecondIdentityCard { get; set; }
public Notes Notes { get; set; }
public string Sign { get; set; }
}
//Note: THIS is the actual class that matches the JSON sample given.
public class ParentObject
{
public string insured_agent_flag { get; set; }
public int Id { get; set; }
public Agent Agent { get; set; }
public BasicData BasicData { get; set; }
public IEnumerable<string> NonOfferedProducts { get; set; }
}
Once the model is correct, then Deserialization works fine for me with the given example (I did this in a unit test, but assuming your string matches your example this should be fine)
//get json
string json = #"
{
""insured_agent_flag"": ""a"",
""id"": ""1"",
""agent"": {
""fullName"": ""John Travolta"",
""mobileNumberPdf"": ""+987654321"",
""mobileNumber"": """",
""identityCard"": {
""identityCardExpirationDate"": null
},
""secondIdentityCard"": {
""identityCardExpirationDate"": null
},
""notes"": {},
""sign"": ""ADVANCED""
},
""basicData"": {
""personType"": ""PERSON"",
""agreeWithCompleteAnalysis"": false,
""investmentInterest"": false
},
""nonOfferedProducts"": [
""PROD_A"",
""PROD_B"",
""PROD_C""
]
}";
var js = new JavaScriptSerializer();
ParentObject obj = js.Deserialize<ParentObject>(json);
//do things...
var rows = obj.NonOfferedProducts.ToList();
Assert.AreEqual(3, rows.Count);
Assert.AreEqual("PROD_A", rows.First());
The asserts pass - This code happily gets the list of strings in the NonOfferedProducts property with the given example.
Obviously if you cannot rely on the consistency of the JSON (either structure or how well-formed it is) then you'll have problems, but that's a different issue.
To answer your question no 2) you are getting the object reference error because the BasicDataClass.nonOfferedProducts is null and you are trying iterate over it , this may be a reason that you are sending the wrong json which JavaScriptSerializer is not able to deserilize.
your 3rd question you can always validate your json with json validators which are there online like https://jsonformatter.org/
This sound very simple. But I can't find how to do it.
I've receiving a bad json from an api. the actual json is inside a string
Instead of
[{\"ProductId\":1,\"ProductName\":\"abuka\",\"Rate\":6.00,\"Quantity\":10.000},{\"ProductId\":2,\"ProductName\":\"abuka\",\"Rate\":6.00,\"Quantity\":10.000},{\"ProductId\":3,\"ProductName\":\"abuka\",\"Rate\":6.00,\"Quantity\":10.000}]
I'm receiving
"[{\"ProductId\":1,\"ProductName\":\"abuka\",\"Rate\":6.00,\"Quantity\":10.000},{\"ProductId\":2,\"ProductName\":\"abuka\",\"Rate\":6.00,\"Quantity\":10.000},{\"ProductId\":3,\"ProductName\":\"abuka\",\"Rate\":6.00,\"Quantity\":10.000}]"
When I try
JsonConvert.DeserializeObject<List<Product>> (jsonString)
I get error Error converting to System.Collections.Generic.List
How can I extract it into a valid JSON string before deserialising?
If you've got a value which is "a string serialized as JSON", then just deserialize that first. Assuming your string genuinely starts and ends with a double quote, you should be fine to call JsonConvert.DeserializeObject<string> to do that unwrapping:
using System;
using System.IO;
using Newtonsoft.Json;
public class Model
{
public string Foo { get; set; }
}
public class Test
{
static void Main()
{
string json = "\"{\\\"foo\\\": \\\"bar\\\"}\"";
Console.WriteLine($"Original JSON: {json}");
string unwrappedJson = JsonConvert.DeserializeObject<string>(json);
Console.WriteLine($"Unwrapped JSON: {unwrappedJson}");
Model model = JsonConvert.DeserializeObject<Model>(unwrappedJson);
Console.WriteLine($"model.Foo: {model.Foo}");
}
}
Thanks Jon Skeet. Referring to your answer, the users who are not using "Newtonsoft.Json" in the application but the Web API which gives the data uses "Newtonsoft.Json" for them below one might be helpful.
Example :
In WEB API - "Newtonsoft.Json" used
The application(might be MVC/ASP.NET WebForms) which consumes WEB API, doesn't use "Newtonsoft.Json". So, they can reference to "System.Web.Extensions" and use using System.Web.Script.Serialization to desialize the json data.
using System.Collections.Generic;
using System.Web.Script.Serialization;
namespace DesrializeJson1ConsoleApp
{
class Program
{
static void Main(string[] args)
{
string jsonstring = "\"[{\\\"ProductId\\\":1,\\\"ProductName\\\":\\\"abuka\\\",\\\"Rate\\\":6.00,\\\"Quantity\\\":10.000},{\\\"ProductId\\\":2,\\\"ProductName\\\":\\\"abuka\\\",\\\"Rate\\\":6.00,\\\"Quantity\\\":10.000},{\\\"ProductId\\\":3,\\\"ProductName\\\":\\\"abuka\\\",\\\"Rate\\\":6.00,\\\"Quantity\\\":10.000}]\"";
var serializer = new JavaScriptSerializer();
var jsonObject = serializer.Deserialize<string>(jsonstring);
List<Product> lstProducts = serializer.Deserialize<List<Product>>(jsonObject);
foreach(var item in lstProducts)
{
Console.WriteLine("ProductId :" + item.ProductId);
Console.WriteLine("ProductName :" + item.ProductName);
Console.WriteLine("Rate :" + item.Rate);
Console.WriteLine("Quantity :" + item.Quantity);
Console.WriteLine("--------------------------");
}
Console.Read();
}
}
public class Product
{
public int ProductId { get; set; }
public string ProductName { get; set; }
public double Rate { get; set; }
public double Quantity { get; set; }
}
}
OUTPUT
I'm currently using a beta API (http://developer.riotgames.com/api/methods) which returns JSON for all the exposed methods.
I've been able to use JSON.NET to deserialize all of these return values so far. However, today I consumed one of their function which returns a JSON that is valid but is in my opinion not correct.
You're probably wondering, why don't you ask it on the beta forum? I have but I haven't received an answer so far and in general this intrigues me.
A snippet of the JSON return:
"1001": {
"name": "Boots of Speed",
"plaintext": "Slightly increases Movement Speed",
"group": "BootsNormal",
"description": "<...
}
The problem I have with this structure is that the ID is used as a "group" without an identifier. I would be able to use this decently if it had
"ItemID" : "1001"
But it doesn't have that. I don't mind manually parsing it but I'd first like to know whether or not this JSON is correct (not just valid).
Do you agree that this is not a clean way of creating a JSON block that contains a list of elements or am I missing something here? So far I haven't seen any comments on the beta forum of this API so I'm really wondering why.
Edit "valid" vs "correct/usable":
I know it's a valid JSON statement. I'm questioning the fact whether this is usable with JSON.NET.
I have the following class definition (with two subclasses):
public class JSONItem
{
[JsonProperty("tags")]
public string[] Tags { get; set; }
[JsonProperty("plaintext")]
public string Plaintext { get; set; }
[JsonProperty("description")]
public string Description { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("into")]
public string[] Into { get; set; }
[JsonProperty("image")]
public JSONItemImage Image { get; set; }
[JsonProperty("colloq")]
public string Colloq { get; set; }
[JsonProperty("gold")]
public JSONItemGold Gold { get; set; }
}
When giving the above JSON block to to JSONConvert.DeserializeObject(json) it throws an error because "1001" is not mentioned in JSONItem.
How do you handle this so that you can use JSON.NET?
A class like this won't work because you have no names to give the properties:
public class JSONItemWrapper
{
[JsonProperty("")]
public string ID { get; set; }
[JsonProperty("")]
public JSONItem MyProperty { get; set; }
}
Edit: "consistent with other methods"
The other methods return blocks where every property is within {} and has an identifier. The most recently added function have this "primary key outside of {}" style.
It is a valid json and you can use a type like Dictionary<string, SomeObject> to deserialize your json.
string json = #"{
""1001"": {
""name"": ""Boots of Speed"",
""plaintext"": ""Slightly increases Movement Speed"",
""group"": ""BootsNormal"",
""description"": ""desc...""
}
}";
var dict = JsonConvert.DeserializeObject<Dictionary<string, MyObject>>(json);
and accesing an item later on by its key can be fast too.
public class MyObject
{
public string name { get; set; }
public string plaintext { get; set; }
public string group { get; set; }
public string description { get; set; }
}
It's annoying when APIs do things like this (using numbers as property names), but all is not lost. Simply deserialize the JSON using Json.NET and then access each of the items using the indexer operator on the parent object.
EDIT:
I almost never create DTOs when deserializing JSON. It's lots of unnecessary boilerplate in most cases. I prefer deserializing to a dynamic object, but that won't be as effective when dealing with property names that begin with digits.
Here is how I would deserialize your sample message:
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace JsonExample
{
internal class Program
{
private static void Main()
{
const string json = #"
{
'1001': {
'name': 'Boots of Speed',
'plaintext': 'Slightly increases Movement Speed',
'group': 'BootsNormal',
'description': '<...'
}
}";
var jObject = JsonConvert.DeserializeObject<JObject>(json);
var plaintext = jObject["1001"]["plaintext"].Value<string>();
Console.WriteLine(plaintext);
}
}
}
When put into http://JSONLint.com,
{
"1001": {
"name": "Boots of Speed",
"plaintext": "Slightly increases Movement Speed",
"group": "BootsNormal",
"description": "<..."
}
}
Validates as JSON.