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
Related
Gurus,
I have a pesky problem with a simple AWS C# Lambda function. How to serialize fields with hyphen in their names? For example:
{ "detail-type" = "ABC", "event-id" = "123" }
If I use the [assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] it is not able to parse it into a field inside a class like this
public class Test
{
public string DetailType { get; set; }
public string EventId { get; set; }
}
I am not sure if either I can substitute the Deserializer with a different one or if I can add attributes to the Field Property in the class so that it can recognize an hyphenated input field and read it.
Any help is greatly appreciated!
I found out the solution! It seems Amazon.Lambda.Serialization.SystemTextJson (version 1.7 and beyond) makes use of the System.Text.Json namespace! So basically we can use the JsonPropertyName attribute to control the deserialization. I have shown an example below.
JSON
{ "detail-type" = "ABC", "event-id" = "abc123" }
C# Class
using System.Text.Json.Serialization;
public class Test
{
[JsonPropertyName("detail-type")]
public string DetailType { get; set; }
[JsonPropertyName("event-id")]
public string EventId { get; set; }
}
Lambda
public string FunctionHandler(Test input, ILambdaContext context)
{
return "Success!";
}
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/
Im getting a Json Data from an API and i have been trying to deserialize.
Json data:
{
"items": [
{
"id": "1",
"name": "samplename",
"AddressList1": {
"City": "Hyd",
"State": "TN",
"Country": "IN"
},
"Age": "10"
},
{
"id": "2",
"name": "samplename2",
"AddressList1": {
"City": "Hydd",
"State": "TN",
"Country": "IN"
},
"Age": "10"
}
],
"paging": {
"cursors": {}
}
}
Entities:
public class AddressList1
{
public string City { get; set; }
public string State { get; set; }
public string Country { get; set; }
}
public class Item
{
public string id { get; set; }
public string name { get; set; }
public AddressList1 addressList1 { get; set; }
public string Age { get; set; }
}
public class Cursors
{
}
public class Paging
{
public Cursors cursors { get; set; }
}
public class Users
{
public List<Item> items { get; set; }
public Paging paging { get; set; }
}
C# code:
JsonConvert.DeserializeObject<List<Users>>(content);
Error Message:
Cannot deserialize the current JSON object (e.g. {"name":"value"})
into type 'System.Collections.Generic.List`1[Entities.Users]'
because the type requires a JSON array (e.g. [1,2,3]) to deserialize
correctly.
where am i doing wrong?
The following is a JSON-object; in your case a User
{ ... }
The following is a JSON-array; in your case an array of User
[ { ... }, { ... } ]
Thus if you want to deserialize the JSON you got into an array of Users this is not possible because you have no array in JSON.
Therefore the right code to deserialize is:
JsonConvert.DeserializeObject<Users>(content);
Furthermore your mapping is erroneous because in JSON there is a property AddressList1 and in the class it is called addressList1
Given your JSON, you would need a POCO object that contains a items member and a paging member.
JsonConvert.DeserializeObject<Users>(content);
should work.
Your Json string is good formatted and the entities are according to Json2Csharp good too.
but your problem is with the instruction JsonConvert.DeserializeObject<List<Users>>(content);
all that json that you have is only ONE User, and you are trying to get a list of them, there is the issue,
you can try instead with:
JsonConvert.DeserializeObject<Users>(content);
Try Below Code
JsonConvert.DeserializeObject<Users>(content);
Your entities(models) look just fine. If you are using, or were to use ASP.NET Web API 2, and your client is using the http verb post for example, this setup would work as Web API takes care of the object deserialization:
public HttpStatusCode Post(Item item)
{
Debug.Write(item.toString());
return HttpStatusCode.OK;
}
If you insist in deserializing manually then use the JavaScriptSerializer library which allows you to do things like:
Item item = new JavaScriptSerializer().Deserialize<Item>(content);
Notice that .Deserialize<T>() takes a generic which in your case it Item.
Hope that helps.
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.
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();
}
}