I have some JSON that can come in two different formats. Sometimes the location value is a string, and sometimes it is an object. This is a sample of the first format:
{
"result": [
{
"upon_approval": "Proceed to Next Task",
"location": "",
"expected_start": ""
}
]
}
Class definitions for this:
public class Result
{
public string upon_approval { get; set; }
public string location { get; set; }
public string expected_start { get; set; }
}
public class RootObject
{
public List<Result> result { get; set; }
}
Here is the JSON in the second format:
{
"result": [
{
"upon_approval": "Proceed to Next Task",
"location": {
"display_value": "Corp-HQR",
"link": "https://satellite.service-now.com/api/now/table/cmn_location/4a2cf91b13f2de00322dd4a76144b090"
},
"expected_start": ""
}
]
}
Class definitions for this:
public class Location
{
public string display_value { get; set; }
public string link { get; set; }
}
public class Result
{
public string upon_approval { get; set; }
public Location location { get; set; }
public string expected_start { get; set; }
}
public class RootObject
{
public List<Result> result { get; set; }
}
When deserializing, I get errors when the JSON format does not match my classes, but I don't know ahead of time which classes to use because the JSON format changes. So how can I dynamically get these two JSON formats to deserialize into one set of classes?
This is how I am deserializing now:
JavaScriptSerializer ser = new JavaScriptSerializer();
ser.MaxJsonLength = 2147483647;
RootObject ro = ser.Deserialize<RootObject>(responseValue);
To solve this problem you'll need to make a custom JavaScriptConverter class and register it with the serializer. The serializer will load the result data into a Dictionary<string, object>, then hand off to the converter, where you can inspect the contents and convert it into a usable object. In short, this will allow you to use your second set of classes for both JSON formats.
Here is the code for the converter:
class ResultConverter : JavaScriptConverter
{
public override IEnumerable<Type> SupportedTypes
{
get { return new List<Type> { typeof(Result) }; }
}
public override object Deserialize(IDictionary<string, object> dict, Type type, JavaScriptSerializer serializer)
{
Result result = new Result();
result.upon_approval = GetValue<string>(dict, "upon_approval");
var locDict = GetValue<IDictionary<string, object>>(dict, "location");
if (locDict != null)
{
Location loc = new Location();
loc.display_value = GetValue<string>(locDict, "display_value");
loc.link = GetValue<string>(locDict, "link");
result.location = loc;
}
result.expected_start = GetValue<string>(dict, "expected_start");
return result;
}
private T GetValue<T>(IDictionary<string, object> dict, string key)
{
object value = null;
dict.TryGetValue(key, out value);
return value != null && typeof(T).IsAssignableFrom(value.GetType()) ? (T)value : default(T);
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
throw new NotImplementedException();
}
}
Then use it like this:
var ser = new JavaScriptSerializer();
ser.MaxJsonLength = 2147483647;
ser.RegisterConverters(new List<JavaScriptConverter> { new ResultConverter() });
RootObject ro = serializer.Deserialize<RootObject>(responseValue);
Here is a short demo:
class Program
{
static void Main(string[] args)
{
string json = #"
{
""result"": [
{
""upon_approval"": ""Proceed to Next Task"",
""location"": {
""display_value"": ""Corp-HQR"",
""link"": ""https://satellite.service-now.com/api/now/table/cmn_location/4a2cf91b13f2de00322dd4a76144b090""
},
""expected_start"": """"
}
]
}";
DeserializeAndDump(json);
Console.WriteLine(new string('-', 40));
json = #"
{
""result"": [
{
""upon_approval"": ""Proceed to Next Task"",
""location"": """",
""expected_start"": """"
}
]
}";
DeserializeAndDump(json);
}
private static void DeserializeAndDump(string json)
{
var serializer = new JavaScriptSerializer();
serializer.RegisterConverters(new List<JavaScriptConverter> { new ResultConverter() });
RootObject obj = serializer.Deserialize<RootObject>(json);
foreach (var result in obj.result)
{
Console.WriteLine("upon_approval: " + result.upon_approval);
if (result.location != null)
{
Console.WriteLine("location display_value: " + result.location.display_value);
Console.WriteLine("location link: " + result.location.link);
}
else
Console.WriteLine("(no location)");
}
}
}
public class RootObject
{
public List<Result> result { get; set; }
}
public class Result
{
public string upon_approval { get; set; }
public Location location { get; set; }
public string expected_start { get; set; }
}
public class Location
{
public string display_value { get; set; }
public string link { get; set; }
}
Output:
upon_approval: Proceed to Next Task
location display_value: Corp-HQR
location link: https://satellite.service-now.com/api/now/table/cmn_location/4a2cf91b13f2de00322dd4a76144b090
----------------------------------------
upon_approval: Proceed to Next Task
(no location)
Related
Given the following json:
[ {"id":"123", ... "data":[{"key1":"val1"}, {"key2":"val2"}], ...}, ... ]
that is part of a bigger tree, how can I deserialize the "data" property into:
List<MyCustomClass> Data { get; set; }
or
List<KeyValuePair> Data { get; set; }
or
Dictionary<string, string> Data { get; set; }
using Json.NET? Either version will do (I prefer List of MyCustomClass though). I already have a class that contains other properties, like this:
public class SomeData
{
[JsonProperty("_id")]
public string Id { get; set; }
...
public List<MyCustomClass> Data { get; set; }
}
where "MyCustomClass" would include just two properties (Key and Value). I noticed there is a KeyValuePairConverter class that sounds like it would do what I need, but I wasn't able to find an example on how to use it. Thanks.
The simplest way is deserialize array of key-value pairs to IDictionary<string, string>:
public class SomeData
{
public string Id { get; set; }
public IEnumerable<IDictionary<string, string>> Data { get; set; }
}
private static void Main(string[] args)
{
var json = "{ \"id\": \"123\", \"data\": [ { \"key1\": \"val1\" }, { \"key2\" : \"val2\" } ] }";
var obj = JsonConvert.DeserializeObject<SomeData>(json);
}
But if you need deserialize that to your own class, it can be looks like that:
public class SomeData2
{
public string Id { get; set; }
public List<SomeDataPair> Data { get; set; }
}
public class SomeDataPair
{
public string Key { get; set; }
public string Value { get; set; }
}
private static void Main(string[] args)
{
var json = "{ \"id\": \"123\", \"data\": [ { \"key1\": \"val1\" }, { \"key2\" : \"val2\" } ] }";
var rawObj = JObject.Parse(json);
var obj2 = new SomeData2
{
Id = (string)rawObj["id"],
Data = new List<SomeDataPair>()
};
foreach (var item in rawObj["data"])
{
foreach (var prop in item)
{
var property = prop as JProperty;
if (property != null)
{
obj2.Data.Add(new SomeDataPair() { Key = property.Name, Value = property.Value.ToString() });
}
}
}
}
See that I khow that Value is string and i call ToString() method, there can be another complex class.
Thanks #Boo for your answer but in my case I needed to take some small adjustements.
This is how my JSON looks like:
{
"rates": {
"CAD": 1.5649,
"CZK": 26.118,
...
},
"base": "EUR",
"date": "2020-08-16"
}
And my DTO looks like the following:
public IDictionary<string, decimal> Rates { get; set; }
public string Base { get; set; }
public DateTime Date { get; set; }
So the only adjustement was to remove the IEnumerable around the IDictionary.
I ended up doing this:
[JsonConverter(typeof(MyCustomClassConverter))]
public class MyCustomClass
{
internal class MyCustomClassConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JObject jObject = JObject.Load(reader);
foreach (var prop in jObject)
{
return new MyCustomClass { Key = prop.Key, Value = prop.Value.ToString() };
}
return null;
}
public override bool CanConvert(Type objectType)
{
return typeof(MyCustomClass).IsAssignableFrom(objectType);
}
}
public string Key { get; set; }
public string Value { get; set; }
}
public class Datum
{
public string key1 { get; set; }
public string key2 { get; set; }
}
public class RootObject
{
public string id { get; set; }
public List<Datum> data { get; set; }
}
i used this wizard as well json2csharp.com to generate class for deserialized
for using that
using RestSharp;
using Newtonsoft.Json;
IRestResponse restSharp= callRestGetMethodby_restSharp(api_server_url);
string jsonString= restSharp.Content;
RootObject rootObj= JsonConvert.DeserializeObject<RootObject>(jsonString);
return Json(rootObj);
if you call rest by restsharp
public IRestResponse callRestGetMethodby_restSharp(string API_URL)
{
var client = new RestSharp.RestClient(API_URL);
var request = new RestRequest(Method.GET);
request.AddHeader("Content-Type", "application/json");
request.AddHeader("cache-control", "no-cache");
IRestResponse response = client.Execute(request);
return response;
}
also you can get this 6 line of restsharp from getpostman.com tools
You can use public IEnumerable<IDictionary<string, string>> Data as the most answers are recommending, but it is not the best idea, since it creates a new dictionary for each array key value item. I recommend to use List<KeyValuePair<string, string>> instead ( or you can create a custom class as well instead of using KeyValuePair )
var json = "[{ \"id\": \"123\", \"data\": [ { \"key1\": \"val1\" }, { \"key2\" : \"val2\" } ] }]";
List<SomeData> listSomeData = JsonConvert.DeserializeObject<List<SomeData>>(json);
public class SomeData
{
[JsonProperty("id")]
public string Id { get; set; }
public List<KeyValuePair<string, string>> Data { get; set; }
[JsonConstructor]
public SomeData(JArray data)
{
Data = data.Select(d => new KeyValuePair<string, string>(
((JObject)d).Properties().First().Name,
((JObject)d).Properties().First().Value.ToString()))
.ToList();
}
}
I have an Azure Server-less Function that serves to take in a JSON payload and work on the records contained. The function works perfectly well to do what is intended except it shouldn't matter the wrapper node name. For example:
{
"Wrapper": [{
"Field1": "Apple",
"Field2": "Peach",
"Field3": "########5",
"Field4": "Kiwi",
}]
}
Should be processed the same way as:
{
"OtherWrapperName": [{
"Column1": "Apple",
"Something": "Peach",
"SomethingElse": "Banana",
"Field4": "Kiwi"
}]
}
Right now it seems to expect the top level node to be called "Wrapper". Here is my attempt at this (some code has been redacted as it was unnecessary for this example):
public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log)
{
string InputData = await req.Content.ReadAsStringAsync();
var inputData = JsonConvert.DeserializeObject<ItemsPayload>(InputData);
var propertiesLookup = new Dictionary<string, ItemUpdate>();
var propertiesRequest = new PropertySearchRequest { Registry = new List<RequestPropertySearch>() };
int recordCounter = 0;
foreach (var item in inputData.Wrapper)
{
foreach (var kvp in item.Where(property => property.Value.StartsWith("#!!!#")))
{
propertiesLookup[recordCounter.ToString() + "|" + kvp.Value] = new ItemUpdate
{
Properties = item,
UpdateKey = kvp.Key
};
propertiesRequest.Registry.Add(new RequestPropertySearch
{
Token = kvp.Value
});
recordCounter++;
}
}
var intermediateRequest = JsonConvert.SerializeObject(propertiesRequest, Formatting.Indented);
HttpResponseMessage response = MakeRequest(serviceUrl, intermediateRequest, securityHeaderName, securityHeaderValue);
var responseBodyAsText = response.Content.ReadAsStringAsync();
var intermediateData = JsonConvert.DeserializeObject<PropertySearchResponse>(responseBodyAsText.Result);
recordCounter = 0;
foreach (var item in intermediateData.Registry)
{
if (item.Value != null)
{
var itemToUpdate = propertiesLookup[recordCounter.ToString() + "|" + item.Token];
itemToUpdate.Properties[itemToUpdate.UpdateKey] = item.Value;
if (directive.ToLower() == "s")
{
itemToUpdate.Properties[$"#{itemToUpdate.UpdateKey}"] = item.Token;
}
// recordCounter++;
}
recordCounter++;
}
var result = JsonConvert.SerializeObject(inputData, Formatting.Indented);
//return req.CreateResponse(HttpStatusCode.OK, "");
return new HttpResponseMessage()
{
Content = new StringContent(result, System.Text.Encoding.UTF8, "application/json")
};
}
Models:
public class ItemsPayload
{
//public string Directive { get; set; }
public List<Dictionary<string, string>> Wrapper { get; set; }
}
public class PropertySearchRequest
{
public List<RequestPropertySearch> Registry { get; set; }
}
public class RequestPropertySearch
{
public string Token { get; set; }
}
public class PropertySearchResponse
{
public List<ResponsePropertySearch> Registry { get; set; }
}
public class ResponsePropertySearch
{
public string Token { get; set; }
public string Value { get; set; }
public string ProcessId { get; set; }
public string Code { get; set; }
public string Remote { get; set; }
public string Message { get; set; }
}
public class ItemUpdate
{
public Dictionary<string, string> Properties { get; set; }
public string UpdateKey { get; set; }
}
I think the ItemsPayload class property "Wrapper" is causing this as if you change that to something else and rename the node in the JSON it works fine, but I want it to be independent of the name of the top level node. Any thoughts?
You could create a simple JsonConverter for your ItemsPayload to handle the varying wrapper name.
public class ItemsPayloadConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(ItemsPayload);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JObject obj = JObject.Load(reader);
ItemsPayload payload = new ItemsPayload();
// Get the first property of the outer JSON object regardless of its name
// and populate the payload from it
JProperty wrapper = obj.Properties().FirstOrDefault();
if (wrapper != null)
{
payload.Wrapper = wrapper.Value.ToObject<List<Dictionary<string, string>>>(serializer);
}
return payload;
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Then, just annotate your ItemsPayload class with a [JsonConverter] attribute like this and it should work with no other changes to your code:
[JsonConverter(typeof(ItemsPayloadConverter))]
public class ItemsPayload
{
public List<Dictionary<string, string>> Wrapper { get; set; }
}
Fiddle: https://dotnetfiddle.net/9q4tgW
I have a class that calls a webservice which returns a JSON string which I need to deserialize into a C# object. I was successfully able to do this; however, I came across a scenario that I am not sure exactly the best way to handle. More specifically, the JSON will either return a List<List<object>> or just a List<object>. I am having a problem when I deserialize if my object is List<object> and the JSON is List<List<object>>. In that case, an exception is thrown.
This is class that I am trying to deserialize:
public class WorkOrderJson
{
public string type { get; set; }
public Properties properties { get; set; }
public Geometry geometry { get; set; }
}
public class Properties
{
public string FeatureType { get; set; }
public string WorkOrderID { get; set; }
public string EqEquipNo { get; set; }
}
For the Geometry class the coordinates returned are the issue from above. If the JSON returned is a List<List<object>> it serializes fine.
public class Geometry
{
public string type { get; set; }
public List<List<double>> coordinates { get; set; }
}
This is how I am performing deserialization:
WorkOrderJson workOrderJson = new JavaScriptSerializer().Deserialize<List<WorkOrderJson>>(responseString);
where responseString is the JSON string returned from web service. Hope this makes sense. If anybody has come across a similar issue, any help would be much appreciated.
Here is an example for List<List<object>> where coordinates is the list:
[
{
"type": "Feature",
"properties": {
"FeatureType": "WORKORDER",
"WorkOrderID": "AMO172-2015-107",
"EqEquipNo": "AC-LIN-001"
},
"geometry": {
"type": "LineString",
"coordinates": [
[
-111.00041804208979,
33.0002148138019
],
[
-111.00027869450028,
33.000143209356054
]
]
},
"symbology": {
"color": "#990000",
"lineWidth": "8"
}
}
]
Here is an example for List<object>:
[
{
"type": "Feature",
"properties": {
"FeatureType": "WORKORDER",
"WorkOrderID": "AMO172-2015-115",
"EqEquipNo": "AC-LIN-001"
},
"geometry": {
"type": "Point",
"coordinates": [
-111.00041804208979,
33.0002148138019
]
}
}
]
So for anyone that cares changing the Geometry Class to the following solved my problem:
public class Geometry
{
public string type { get; set; }
public object coordinates { get; set; }
}
Just changed list to object. Then at runtime i can check whether the object is list of lists or just a list and proceed.
Create a custom JavaScriptConverter and register it with your JavaScriptSerializer.
You would then deserialize like this:
var converter = new JavaScriptSerializer();
converter.RegisterConverters(new List<JavaScriptConverter>() {new GeometryConverter()});
var workOrderJson = converter.Deserialize<List<WorkOrderJson>>(response);
This converter would work:
public class GeometryConverter : JavaScriptConverter
{
public override IEnumerable<Type> SupportedTypes
{
get { return new List<Type>(new Type[] {typeof(Geometry)}); }
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
Geometry geometry = obj as Geometry;
if (geometry != null)
{
// Create the representation
var result = new Dictionary<string, object>();
if (geometry.coordinates.Count == 1)
{
result.Add("type", "Point");
result.Add("coordinates", geometry.coordinates[0]);
}
else if (geometry.coordinates.Count > 1)
{
result.Add("type", "LineString");
result.Add("coordinates", geometry.coordinates);
}
return result;
}
return new Dictionary<string, object>();
}
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
if (dictionary == null)
throw new ArgumentNullException("dictionary");
Geometry geometry = null;
if (type == typeof(Geometry))
{
geometry = new Geometry();
geometry.type = (string)dictionary["type"];
geometry.coordinates = new List<List<double>>();
if ( geometry.type == "Point")
{
ArrayList arrayList = (ArrayList)dictionary["coordinates"];
geometry.coordinates.Add(ConvertCoordinates(arrayList));
}
else if (geometry.type == "LineString")
{
geometry.type = "LineString";
ArrayList coordinatesList = (ArrayList)dictionary["coordinates"];
foreach (ArrayList arrayList in coordinatesList)
{
geometry.coordinates.Add(ConvertCoordinates(arrayList));
}
}
}
return geometry;
}
private List<double> ConvertCoordinates(ArrayList coordinates)
{
var list = new List<double>();
foreach (var coordinate in coordinates)
{
list.Add((double)System.Convert.ToDouble(coordinate));
}
return list;
}
}
If I understand and you are trying to deserialize a List<List<a>> as a List<a>, it should error. You're telling it to deserialize it into something other than what it was before serialization. I would recommend either propagating some indication of which it is along with the string so you can check and deserialize as that type, or wrap the deserialization attempts and try one first, then the other if it fails.
Edit per update
public class WorkOrderJson<T>
{
public Geometry<T> geometry { get; set; }
public WorkOrderJson<List<T>> Promote()
{
var temp = new WorkOrderJson<List<T>>();
temp.geometry = geometry.Promote();
return temp;
}
}
public class Geometry<T>
{
public T coordinates { get; set; }
public Geometry<List<T>> Promote()
{
var temp = new Geometry<List<T>>();
temp.coordinates = new List<T>(){ coordinates };
return temp;
}
}
public List<WorkOrder<List<List<double>>>> Deserialize(string x)
{
try
{
return new JavaScriptSerializer().Deserialize<List<WorkOrderJson<List<List<double>>>>>(x);
}
catch(InvalidOperationException ex)
{
return new JavaScriptSerializer().Deserialize<List<WorkOrderJson<List<double>>>>(x).Select(workOrder => workOrder.Promote());
}
}
I am trying to deserialize the json string from Rest webserive in C#. Following is jsonstring and classes I used. But after deserialize it is giving null/empty value on server.But in local its working as expected. So what's going wrong with me?
Json String
{
"error": false,
"parameters": [
{
"Programs": "ZZ_UNUSED_EU-01pp-Q669;EU-PPP-O003;ZZ_UNUSED_EU-PPP-CO05;100000;pbse;EU-m110-2007;EU-zz33-L053;EU-zz21-z084;ZZ_UNUSED_EU-01pp-Q875;EU-zz05-L029;ZZ_UNUSED_EU-RAD-003;ZZ_UNUSED_EU-RAD-004;ZZ_UNUSED_EU-RAD-007;ZZ_UNUSED_EU-RAD-012;ZZ_UNUSED_EU-RAD-015;ZZ_UNUSED_EU-zz22-RF21;ZZ_UNUSED_EU-PPP-CO130;ZZ_UNUSED_EU-01pp-Q962;ZZ_UNUSED_EU-01pp-Q963;EU-LandR-COM;EU-01pp-O960;EU-SI-HP-INTL;G_PC_SQ;G_Survey_Inv_TPE-FY11;G_MADO_3Com;G_eLocator_AIS;G_eLocator_ATP;G_eLocator_SCE;G_eLocator_TECI;G_L&R_SCREENED;G_L&R_CASE_OPEN;EU-MDF-Tool;EU-DI-SPT-FLASHPRO;EU-DI-SPT-FLASHPRO-FY11;EU-DI-SPT-FLASHPRO-FY12;G_SPT_PCLM_Prospect;G_SPT_PCLM_Enrolled;SPEC_P1_S1_GOLD_PPS_CAN;G_SPT_Joint_Business_Planning;EU_RET_CE_SCOPE;G_PARTNER_HPQ;ZZ_UNUSED_EU-01pp-O972;SPEC_P1_SERV_SILV_CAN;SPEC_P1_STOR_SILV_CAN;SPEC_P1_NW_SILV_CAN;SPEC_P1_S1_SILV_CAN;PM_P1_PLAT_PART_CAN;PM_P1_SILV_PART_PPS_SM_CAN;SPEC_P1_WK_GOLD_CAN;ZZ_UNUSED_EU-01pp-Q018;"
},
{
"PartnerSubTypes": "G_Tgt_Commercial_T2_Reseller_S;SSP;Supplies Commercial;"
},
{
"CompanyNumber": "29682"
},
{
"PartnerNameHQ": "151070026"
},
{
"SiteIdentifier": "UPP"
},
{
"LastName": "Isaksson"
},
{
"HPInternalUser": "F"
},
{
"PhysAdLine1": "?STRA HAMNEN 1"
},
{
"HPPUserId": "989d35449261b4348e2f477260b1eacc"
},
{
"PartnerName": "Misco AB"
},
{
"PhysAdLine2": ""
},
{
"PhysAdLine3": ""
},
{
"PrimaryChannelSegment": "TDL"
},
{
"HPOrg": "HP Sweden"
},
{
"LastLoginDate": "1426841482160"
},
{
"PartnerPhone": ""
},
{
"PartnerLegalName": "Misco AB"
},
{
"HPOrgs": "Default Organization;HP Sweden;"
},
{
"FirstName": "Joakim"
},
{
"CountryCode": "SE"
},
{
"PreferredLanguageCode": "en"
},
{
"IsPartnerAdmin": "T"
},
{
"PartnerProIdHQ": "1-2GF-564"
},
{
"Accreditations": "G_PM_P1_PLAT_PAR_PP_BU;G_SEGT_EG_SERV_GROWTH;G_SEGT_EG_TS_GROWTH;G_FC_AGR_T2;G_S1_Prospect;G_GPL_P1_PPS_SM;ZZ_UNUSED_EU-zz46-c103;EU-02pp-O138;G_P1_Specialist_Enrolled;G_P1_Specialist_Candidate;G_S1_EXP;G_GPL_P1_EG_BUS;G_GPL_P1_PPS;G_PM_P1_PLAT_PAR;G_PM_P1_BUS_SP_EG_BU;G_GPL_P1_WK;G_P1_Preferred_Candidate;G_P1_Specialist_Enrolled;G_S1_Candidate;G_SEGT_PAN-EG_GROWTH;G_SEGT_EG_NW_GROWTH;G_SEGT_EG_STOR_GROWTH;FY15_P1_STOR_SILV_TGT;FY15_P1_S1_SILV_TGT;FY15_P1_NW_SILV_TGT;G_GPL_P1_S1_P;G_S1_Prospect;FY15_P1_SERV_SILV_TGT;G_PM_P1_BUS_SP_EG;G_PM_P1_PLAT_PAR_PP;G_PM_P1_SILV_PAR_PP_SM;G_SPEC_P1_S1_GOLD_P;G_SPEC_P1_WK_GOLD;ZZ_UNUSED_EU-I&P;ZZ_UNUSED_EU-01pp-Q586;"
},
{
"PhysCountry": "Sweden"
},
{
"PhysCity": "LIDK?PING"
},
{
"PartnerProId": "1-2GF-564"
},
{
"Tier": ""
},
{
"Email": "itg.itg.joakim.isaksson#misco.se"
},
{
"PhysPostalCode": "531 40"
},
{
"PartnerFax": ""
}
]
}
Rest webservice call
//string requestUrl = "http://localhost:8080/RestWSPartner/parameter";
string requestUrl = "https://upp-itg-moonshot-gslb.austin.hp.com/delegate/esmtokenvalidation/v1?token="+tokenId;
Log.WriteInfo("requestUrl in RestWS==>" + requestUrl);
do
{
HttpWebRequest request = HttpWebRequest.Create(requestUrl) as HttpWebRequest;
request.Method = "GET";
request.ContentType = "application/json; charset=UTF-8";//charset=UTF-8";
DataContractJsonSerializer jsonSerializer = new DataContractJsonSerializer(typeof(PartnerData));
HttpWebResponse response = request.GetResponse() as HttpWebResponse;
Stream receiveStream = response.GetResponseStream();
restResponse = jsonSerializer.ReadObject(receiveStream) as PartnerData;
Log.WriteInfo("restResponse.error ==>" + restResponse.error);
Log.WriteInfo("restResponse.message ==>" + restResponse.message);
if (restResponse.error.Equals("false"))
{
// Log.WriteInfo("restResponse===UserId>" + restResponse.parameters.HPPUserId);
Log.WriteInfo("restResponse===PartnerProId>" + restResponse.parameters.PreferredLanguageCode);
Log.WriteInfo("restResponse===PartnerProId>" + restResponse.parameters.FirstName);
Log.WriteInfo("success.. Break");
break;
}
my class
namespace ProductguideNordic
{
[DataContract]
public class PartnerData
{
[DataMember(Name = "error")]
public string error { get; set; }
[DataMember(Name = "message")]
public string message { get; set; }
[DataMember(Name = "parameters")]
public Paramter parameters { get; set; }
}
[DataContract]
public class Paramter
{
[DataMember(Name = "Programs")]
public string Programs { set; get; }
[DataMember(Name = "PartnerSubTypes")]
public string PartnerSubTypes { set; get; }
[DataMember(Name = "CompanyNumber")]
public string CompanyNumber { set; get; }
[DataMember(Name = "PartnerNameHQ")]
public string PartnerNameHQ { set; get; }
[DataMember(Name = "SiteIdentifier")]
public string SiteIdentifier { set; get; }
[DataMember(Name = "LastName")]
public string LastName { set; get; }
[DataMember(Name = "HPInternalUser")]
public string HPInternalUser { set; get; }
[DataMember(Name = "PhysAdLine1")]
public string PhysAdLine1 { set; get; }
[DataMember(Name = "HPPUserId")]
public string HPPUserId { set; get; }
[DataMember(Name = "PartnerName")]
public string PartnerName { set; get; }
[DataMember(Name = "PhysAdLine2")]
public string PhysAdLine2 { set; get; }
[DataMember(Name = "PhysAdLine3")]
public string PhysAdLine3 { set; get; }
[DataMember(Name = "PrimaryChannelSegment")]
public string PrimaryChannelSegment { set; get; }
[DataMember(Name = "HPOrg")]
public string HPOrg { set; get; }
[DataMember(Name = "LastLoginDate")]
public string LastLoginDate { set; get; }
[DataMember(Name = "PartnerPhone")]
public string PartnerPhone { set; get; }
[DataMember(Name = "PartnerLegalName")]
public string PartnerLegalName { set; get; }
[DataMember(Name = "HPOrgs")]
public string HPOrgs { set; get; }
[DataMember(Name = "FirstName")]
public string FirstName { set; get; }
[DataMember(Name = "CountryCode")]
public string CountryCode { set; get; }
[DataMember(Name = "PreferredLanguageCode")]
public string PreferredLanguageCode { set; get; }
[DataMember(Name = "IsPartnerAdmin")]
public string IsPartnerAdmin { set; get; }
[DataMember(Name = "PartnerProIdHQ")]
public string PartnerProIdHQ { set; get; }
[DataMember(Name = "Accreditations")]
public string Accreditations { set; get; }
[DataMember(Name = "PhysCountry")]
public string PhysCountry { set; get; }
[DataMember(Name = "physCity")]
public string physCity { set; get; }
[DataMember(Name = "PartnerProId")]
public string PartnerProId { set; get; }
[DataMember(Name = "Tier")]
public string Tier { set; get; }
[DataMember(Name = "Email")]
public string Email { set; get; }
[DataMember(Name = "PhysPostalCode")]
public string PhysPostalCode { set; get; }
[DataMember(Name = "PartnerFax")]
public string PartnerFax { set; get; }
[DataMember(Name = "PortalSessionId")]
public string PortalSessionId { set; get; }
}
}
In my local with local URL, im able to deserialize the json string and assigned to partnerdata. In server (windows 2012) after deployed into IIS(8.5) parameters values are null/ empty and there was no error while deserialize . Rest WS sending expected response's. and restResponse.error this value deserialized correctly but parameters are not deserialized.
So what's going wrong with me?
In the JSON string you show, "parameters" is an array of name/value pairs:
{
"error": false,
"parameters": [
{
"Programs": "ZZ_UNUSED_EU-01pp-Q669;EU-PPP-O003;ZZ_UNUSED_EU-PPP-CO05;100000;pbse;EU-m110-2007;EU-zz33-L053;EU-zz21-z084;ZZ_UNUSED_EU-01pp-Q875;EU-zz05-L029;ZZ_UNUSED_EU-RAD-003;ZZ_UNUSED_EU-RAD-004;ZZ_UNUSED_EU-RAD-007;ZZ_UNUSED_EU-RAD-012;ZZ_UNUSED_EU-RAD-015;ZZ_UNUSED_EU-zz22-RF21;ZZ_UNUSED_EU-PPP-CO130;ZZ_UNUSED_EU-01pp-Q962;ZZ_UNUSED_EU-01pp-Q963;EU-LandR-COM;EU-01pp-O960;EU-SI-HP-INTL;G_PC_SQ;G_Survey_Inv_TPE-FY11;G_MADO_3Com;G_eLocator_AIS;G_eLocator_ATP;G_eLocator_SCE;G_eLocator_TECI;G_L&R_SCREENED;G_L&R_CASE_OPEN;EU-MDF-Tool;EU-DI-SPT-FLASHPRO;EU-DI-SPT-FLASHPRO-FY11;EU-DI-SPT-FLASHPRO-FY12;G_SPT_PCLM_Prospect;G_SPT_PCLM_Enrolled;SPEC_P1_S1_GOLD_PPS_CAN;G_SPT_Joint_Business_Planning;EU_RET_CE_SCOPE;G_PARTNER_HPQ;ZZ_UNUSED_EU-01pp-O972;SPEC_P1_SERV_SILV_CAN;SPEC_P1_STOR_SILV_CAN;SPEC_P1_NW_SILV_CAN;SPEC_P1_S1_SILV_CAN;PM_P1_PLAT_PART_CAN;PM_P1_SILV_PART_PPS_SM_CAN;SPEC_P1_WK_GOLD_CAN;ZZ_UNUSED_EU-01pp-Q018;"
},
{
"PartnerSubTypes": "G_Tgt_Commercial_T2_Reseller_S;SSP;Supplies Commercial;"
},
Your code is attempting to deserialize parameters as a single object with many properties, e.g.:
{
"error": false,
"parameters": {
"Programs": "ZZ_UNUSED_EU-01pp-Q669;EU-PPP-O003;ZZ_UNUSED_EU-PPP-CO05;100000;pbse;EU-m110-2007;EU-zz33-L053;EU-zz21-z084;ZZ_UNUSED_EU-01pp-Q875;EU-zz05-L029;ZZ_UNUSED_EU-RAD-003;ZZ_UNUSED_EU-RAD-004;ZZ_UNUSED_EU-RAD-007;ZZ_UNUSED_EU-RAD-012;ZZ_UNUSED_EU-RAD-015;ZZ_UNUSED_EU-zz22-RF21;ZZ_UNUSED_EU-PPP-CO130;ZZ_UNUSED_EU-01pp-Q962;ZZ_UNUSED_EU-01pp-Q963;EU-LandR-COM;EU-01pp-O960;EU-SI-HP-INTL;G_PC_SQ;G_Survey_Inv_TPE-FY11;G_MADO_3Com;G_eLocator_AIS;G_eLocator_ATP;G_eLocator_SCE;G_eLocator_TECI;G_L&R_SCREENED;G_L&R_CASE_OPEN;EU-MDF-Tool;EU-DI-SPT-FLASHPRO;EU-DI-SPT-FLASHPRO-FY11;EU-DI-SPT-FLASHPRO-FY12;G_SPT_PCLM_Prospect;G_SPT_PCLM_Enrolled;SPEC_P1_S1_GOLD_PPS_CAN;G_SPT_Joint_Business_Planning;EU_RET_CE_SCOPE;G_PARTNER_HPQ;ZZ_UNUSED_EU-01pp-O972;SPEC_P1_SERV_SILV_CAN;SPEC_P1_STOR_SILV_CAN;SPEC_P1_NW_SILV_CAN;SPEC_P1_S1_SILV_CAN;PM_P1_PLAT_PART_CAN;PM_P1_SILV_PART_PPS_SM_CAN;SPEC_P1_WK_GOLD_CAN;ZZ_UNUSED_EU-01pp-Q018;"
"PartnerSubTypes": ""G_Tgt_Commercial_T2_Reseller_S;SSP;Supplies Commercial;""
In other words, the JSON you are receiving for "parameters"corresponds to a List<Dictionary<string, string>> with UseSimpleDictionaryFormat = false, rather than a single POCO with various properties.
Update
Given that this is a legacy application using an older version of .Net (possibly with some configuration issues, as the var keyword is causing you to have a compiler error), I am going to suggest switching to JavaScriptSerializer which is also a built-in .Net component. Change your data model as follows:
public class PartnerDataDictionary
{
public PartnerDataDictionary()
{
this.ParameterDictionary = new Dictionary<string, string>();
}
string _error;
public string error { get { return _error; } set { _error = value; } }
string _message;
public string message { get { return _message; } set { _message = value; } }
[System.Web.Script.Serialization.ScriptIgnore]
public Dictionary<string, string> ParameterDictionary { get; set; }
public List<Dictionary<string, string>> parameters
{
get
{
List<Dictionary<string, string>> dictList = new List<Dictionary<string, string>>();
foreach (KeyValuePair<string, string> pair in ParameterDictionary)
{
Dictionary<string, string> subDict = new Dictionary<string,string>(1);
subDict[pair.Key] = pair.Value;
dictList.Add(subDict);
}
return dictList;
}
set
{
if (value == null)
{
ParameterDictionary = new Dictionary<string, string>();
return;
}
Dictionary<string, string> dict = new Dictionary<string, string>();
foreach (Dictionary<string, string> entry in value)
foreach (KeyValuePair<string, string> pair in entry)
dict.Add(pair.Key, pair.Value);
ParameterDictionary = dict;
}
}
public string GetParameter(string key)
{
string value;
if (ParameterDictionary.TryGetValue(key, out value))
return value;
return null;
}
public void SetParameter(string key, string value)
{
ParameterDictionary[key] = value;
}
// Add other properties as needed, marking them as `ScriptIgnore`:
[System.Web.Script.Serialization.ScriptIgnore]
public string Programs
{
get { return GetParameter("Programs"); }
set { SetParameter("Programs", value); }
}
}
What I am doing here is reading the JSON as a list of dictionaries, then combining them into a dictionary in the setter for ease of use.
Then you can read and write your JSON parameters as follows:
PartnerDataDictionary data = new System.Web.Script.Serialization.JavaScriptSerializer().Deserialize<PartnerDataDictionary>(json);
string programs = data.Programs;
Debug.WriteLine(programs);
Original Answer
You could simply change your PartnerData class to have such a list of dictionaries. If you want to continue to use your current data model, and also use DataContractJsonSerializer, you are going to need to introduce a data contract surrogate to do the conversion:
public class ObjectToPropertyDictionaryArraySurrogate<T> : IDataContractSurrogate
{
class ListDictionaryArray : List<Dictionary<string, string>>
{
}
#region IDataContractSurrogate Members
public object GetCustomDataToExport(Type clrType, Type dataContractType)
{
throw new NotImplementedException();
}
public object GetCustomDataToExport(MemberInfo memberInfo, Type dataContractType)
{
throw new NotImplementedException();
}
public Type GetDataContractType(Type type)
{
if (type == typeof(T))
return typeof(ListDictionaryArray);
return type;
}
public object GetDeserializedObject(object obj, Type targetType)
{
if (obj is ListDictionaryArray)
{
var array = (ListDictionaryArray)obj;
var dict = array.SelectMany(pair => pair).ToDictionary(pair => pair.Key, pair => pair.Value);
var json = new System.Web.Script.Serialization.JavaScriptSerializer().Serialize(dict);
return DataContractJsonSerializerHelper.GetObject<T>(json, new DataContractJsonSerializerSettings { UseSimpleDictionaryFormat = true });
}
return obj;
}
public void GetKnownCustomDataTypes(Collection<Type> customDataTypes)
{
throw new NotImplementedException();
}
public object GetObjectToSerialize(object obj, Type targetType)
{
if (obj.GetType() == typeof(T))
{
var json = DataContractJsonSerializerHelper.GetJson((T)obj, new DataContractJsonSerializerSettings { UseSimpleDictionaryFormat = true });
var dict = new System.Web.Script.Serialization.JavaScriptSerializer().Deserialize<Dictionary<string, string>>(json);
var array = new ListDictionaryArray();
array.AddRange(dict.Select(pair => new[] { pair }.ToDictionary(p => p.Key, p => p.Value)));
return array;
}
return obj;
}
public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData)
{
throw new NotImplementedException();
}
public System.CodeDom.CodeTypeDeclaration ProcessImportedType(System.CodeDom.CodeTypeDeclaration typeDeclaration, System.CodeDom.CodeCompileUnit compileUnit)
{
throw new NotImplementedException();
}
#endregion
}
public static class DataContractJsonSerializerHelper
{
public static string GetJson<T>(T obj, DataContractJsonSerializer serializer)
{
using (var memory = new MemoryStream())
{
serializer.WriteObject(memory, obj);
memory.Seek(0, SeekOrigin.Begin);
using (var reader = new StreamReader(memory))
{
return reader.ReadToEnd();
}
}
}
public static string GetJson<T>(T obj)
{
var serializer = new DataContractJsonSerializer(typeof(T));
return GetJson(obj, serializer);
}
public static string GetJson<T>(T obj, DataContractJsonSerializerSettings settings)
{
var serializer = new DataContractJsonSerializer(obj.GetType(), settings);
return GetJson<T>(obj, serializer);
}
public static T GetObject<T>(string json, DataContractJsonSerializer serializer)
{
using (var stream = GenerateStreamFromString(json))
{
var obj = serializer.ReadObject(stream);
return (T)obj;
}
}
public static T GetObject<T>(string json)
{
var serializer = new DataContractJsonSerializer(typeof(T));
return GetObject<T>(json, serializer);
}
public static T GetObject<T>(string json, DataContractJsonSerializerSettings settings)
{
var serializer = new DataContractJsonSerializer(typeof(T), settings);
return GetObject<T>(json, serializer);
}
private static MemoryStream GenerateStreamFromString(string value)
{
return new MemoryStream(Encoding.Unicode.GetBytes(value ?? ""));
}
}
And then use it like:
var settings = new DataContractJsonSerializerSettings
{
UseSimpleDictionaryFormat = true,
DataContractSurrogate = new ObjectToPropertyDictionaryArraySurrogate<Paramter>(),
};
DataContractJsonSerializer jsonSerializer = new DataContractJsonSerializer(typeof(PartnerData), settings);
This question already has answers here:
How can I parse a JSON string that would cause illegal C# identifiers?
(3 answers)
Closed 8 years ago.
I am trying to deserialize a Json response from an API.
The data looks like this
{
"response": {
"6112": {
"ID": 6112,
"Title": "AdditionalPhotos"
},
"5982": {
"ID": 5982,
"Title": "BikeRide"
},
"total_records": "20",
"returned_count": 10,
"returned_records": "1-10"
}
}
C# class:
public class Products
{
public class Product
{
public string Id { get; set; }
public string Title { get; set; }
}
public Product product { get; set; }
}
public class ss
{
public Dictionary<string, Products.Product> Response { get; set; }
public string total_records { get; set; }
}
Serialization code
ss res = Newtonsoft.Json.JsonConvert.DeserializeObject<ss>(jsonData());
I can get it to work without the total_records entry and below by deserializng to a Dictionary <string , Product>. But I cannot figure out how to get it to work. This is the error I get
Error converting value "20" to type 'Products+Product'. Path 'response.total_records'
I know why I get the error, but I'm unsure how I can proceed without going in and substringing from total_records down. I have no control over the API data.
Edit: you guys are fast, I was still getting to putting the classes up
First you json is not valid one, it should look like this
{
"response":{
"6112":{
"ID":"6112",
"Title":"Additional Photos",
},
"5982":{
"ID":"5982",
"Title":"Bike Ride",
},
"total_records": "20",
"returned_count": "10",
"returned_records": "1-10",
}
}
If you mean the response to contain list it should look like this
{
"response":{
"myArray": [
{
"ID":"6112",
"Title":"Additional Photos",
},
{
"ID":"5982",
"Title":"Bike Ride",
}
],
"total_records": "20",
"returned_count": "10",
"returned_records": "1-10",
}
}
So your code look like this
public class MyArray
{
public string ID { get; set; }
public string Title { get; set; }
}
public class Response
{
public List<MyArray> myArray { get; set; }
public string total_records { get; set; }
public string returned_count { get; set; }
public string returned_records { get; set; }
}
public class RootObject
{
public Response response { get; set; }
}
If you have control over API response then please refer to Mzf's answer.
If you don't have control over API then it may not be possible to do this particular deserialization on one go. You might have to loop.
Here's my take.
Update
Modified my approach:
Created a class Response which inherits from Dictionary<string, Product>, and added the metadata parts like total_records, records_count to it's public properties. And created a JsonConverter that can deserialize JObject to Response class.
The logic used for deserialization is quite simple:
Extract the metadata parts like total_records, records_count to variables.
Then remove those metadata from the JObject, so that the key values becomes homogeneous.
Now Json.net will be easily able to serialize JObject to Response object, as key values are homogenous.
Assign the metadata extracted previously to the properties of Response object
public void Deserialize()
{
var json = #"{
'response':{
'6112':{
'ID':6112,
'Title':'Additional Photos',
},
'5982':{
'ID':5982,
'Title':'Bike Ride',
},
'total_records': '20',
'returned_count': 10,
'returned_records': '1-10',
}
}";
var responseObj = Newtonsoft.Json.JsonConvert.DeserializeObject<ss>(json, new ResponseConverter());
}
public class Response : Dictionary<string, Product>
{
public int total_records { get; set; }
public int returned_count { get; set; }
public string returned_records { get; set; }
}
public class Product
{
public string Id { get; set; }
public string Title { get; set; }
}
public class ss
{
public Response Response { get; set; }
}
public class ResponseConverter : Newtonsoft.Json.JsonConverter
{
private Response CreateResponse(Newtonsoft.Json.Linq.JObject jObject)
{
//preserve metadata values into variables
int total_records = jObject["total_records"].ToObject<int>();
var returned_records = jObject["returned_records"].ToObject<string>();
var returned_count = jObject["returned_count"].ToObject<int>();
//remove the unwanted keys
jObject.Remove("total_records");
jObject.Remove("returned_records");
jObject.Remove("returned_count");
//once, the metadata keys are removed, json.net will be able to deserialize without problem
var response = jObject.ToObject<Response>();
//Assign back the metadata to response object
response.total_records = total_records;
response.returned_count = returned_count;
response.returned_records = returned_records;
//.. now person can be accessed like response['6112'], and
// metadata can be accessed like response.total_records
return response;
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Response);
}
public override object ReadJson(Newtonsoft.Json.JsonReader reader, Type objectType, object existingValue, Newtonsoft.Json.JsonSerializer serializer)
{
var jObject = Newtonsoft.Json.Linq.JObject.Load(reader);
Response target = CreateResponse(jObject);
serializer.Populate(jObject.CreateReader(), target);
return target;
}
public override void WriteJson(Newtonsoft.Json.JsonWriter writer, object value, Newtonsoft.Json.JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
In my opinion this is how the JSON file should look like:
{
"response": {
"5982": {
"ID": 5982,
"Title": "BikeRide"
},
"6112": {
"ID": 6112,
"Title": "AdditionalPhotos"
},
"total_records": "20",
"returned_count": 10,
"returned_records": "1-10"
}
}
and this is how the class should look like
public class __invalid_type__5982
{
public int ID { get; set; }
public string Title { get; set; }
}
public class __invalid_type__6112
{
public int ID { get; set; }
public string Title { get; set; }
}
public class Response
{
public __invalid_type__5982 __invalid_name__5982 { get; set; }
public __invalid_type__6112 __invalid_name__6112 { get; set; }
public string total_records { get; set; }
public int returned_count { get; set; }
public string returned_records { get; set; }
}
public class RootObject
{
public Response response { get; set; }
}