newtonsoft json deserializer not working for property with no set - c#

I'm facing a null exception when de-serializing the class below:
[Serializable]
public class myClass
{
public IList<string> ResidentialAddressToPrint
{
get
{
var list = new List<string>();
if (!string.IsNullOrWhiteSpace(ResidentialAddress.AddressLine1))
list.Add(ResidentialAddress.AddressLine1);
if (!string.IsNullOrWhiteSpace(ResidentialAddress.AddressLine2))
list.Add(ResidentialAddress.AddressLine2);
if (!string.IsNullOrWhiteSpace(ResidentialAddress.Suburb))
list.Add(ResidentialAddress.Suburb);
if (!string.IsNullOrWhiteSpace(ResidentialAddress.State))
list.Add(ResidentialAddress.State);
if (!string.IsNullOrWhiteSpace(ResidentialAddress.Postcode))
list.Add(ResidentialAddress.Postcode);
return list;
}
}
[Serializable]
public class RegisterRebateAddressModel
{
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
public string Suburb { get; set; }
public string State { get; set; }
public string Postcode { get; set; }
}
*Newtonsoft.Json.JsonSerializationException: Error getting value from 'myClass'. ---> System.NullReferenceException: Object reference not set to an instance of an object.
at myClass.get_ResidentialAddressToPrint()*
Can someone clarify below:
Does newtonsoft requires the mandatory Set property?
Also noticed it fails for private set; property. Is this feature by design.
What's the best way to solve this issue as we have many properties with no setter.

Related

JsonConvert.DeserializeObject defaults to first Enum item

I have the following data object.
public class Response<T> where T : MyBaseDTO
{
public bool result
{
get; set;
}
public List<Message> messages
{
get; set;
}
public List<T> data
{
get; set;
}
}
public class Message
{
public Message(MessageTypeEnum type)
{
this.typeEnum = type;
}
public string type
{
get
{
return typeEnum.ToString();
}
}
public MessageTypeEnum typeEnum
{
get; set;
}
public int index
{
get; set;
}
public string field
{
get; set;
}
public string code
{
get; set;
}
public string message
{
get; set;
}
public string messageValue
{
get; set;
}
public enum MessageTypeEnum
{
WARNING,
INFO,
ERROR
}
When I use RestSharp to call the API, the deserialized Data is null with following error(Content contains correct response in string):
" ErrorMessage "Each parameter in the deserialization constructor on type 'Response.Message' must bind to an object property or field on deserialization."
I found the following post and added empty constructor to my object.
Error: Each parameter in constructor must bind to an object property or field on deserialization
public class Message
{
public Message()
{
}
public Message(MessageTypeEnum type)
{
this.typeEnum = type;
}
public string type
{
get
{
return typeEnum.ToString();
}
}
public MessageTypeEnum typeEnum
{
get; set;
}
public int index
{
get; set;
}
public string field
{
get; set;
}
public string code
{
get; set;
}
public string message
{
get; set;
}
public string messageValue
{
get; set;
}
public enum MessageTypeEnum
{
WARNING,
INFO,
ERROR
}
Added empty constructor resolved my issue with RestSharp. However I just noticed it failed some unit tests in my other project that uses Message Object.
Here is the code
using var reader = new StreamReader(result.ResponseMessage.Content.ReadAsStream());
body = reader.ReadToEnd();
Response<Test> responseObject = JsonConvert.DeserializeObject<Response<Test>>(body);
"body" looks like something below which is expected.
body "{\"result\":true,\"messages\":[{\"type\":\"ERROR\",\"index\":0,\"field\":\"ID\",\"code\":\"MISSING-ID\",\"message\":\"The entry is incomplete.",\"messageValue\":null}],\"data\":[{\"....
However, after the deserialization, "Type" became WARNING instead ERROR and hence it failed the unit test.
Looks like it defaults to first one of the Enum.
public enum MessageTypeEnum
{
WARNING,
INFO,
ERROR
}
Does anyone knows what's going on here and how to fix the issue?
since you have type as string too, you have to fix your Message costructor by adding attribute
[JsonConstructor]
public Message(MessageTypeEnum type)
{
this.typeEnum = type;
}
after this everything is working properly
Your code is not complete and your JSON example is malformed, so I am unable to provide an example with your exact code. That said, you should be able to suss out the details you need to fix your particular issue.
First, when you are deserializing to a class you must have an empty constructor as required by the deserializer. The deserializer will not be passing any parameters to your constructor, so you can't depend on them.
Second, to read/write a class property it must have both a get and set. The deserializer will not be able to fully work with properties that do not have both get and set.
One possible solution, is to create a string property that contains both get and set, and then create an additional property with only the get that is typed to the Enum.
The root object is basically your Response object, but as you didn't provide that code I made the RootObject.
public class RootObject
{
public bool result { get; set; }
public Message[] messages { get; set; }
}
public class Message
{
// string property to allow deserialization
public string type
{
get; set;
}
// read only property typed to the enum that will
// refer to the 'type' property for conversion to the enum
public MessageTypeEnum typeEnum
{
get
{
switch (type.ToUpper())
{
case nameof(MessageTypeEnum.WARNING):
return MessageTypeEnum.WARNING;
case nameof(MessageTypeEnum.INFO):
return MessageTypeEnum.INFO;
case nameof(MessageTypeEnum.ERROR):
return MessageTypeEnum.ERROR;
default:
return MessageTypeEnum.UNKNOWN;
// return UNKNOWN or throw an out of range exception?
}
}
}
public int index
{
get; set;
}
public string field
{
get; set;
}
public string code
{
get; set;
}
public enum MessageTypeEnum
{
WARNING,
INFO,
ERROR
}
}

Deserialize Json for CRM Entities

I have the following Json defining a specific configuration which has to be stored in custom CRM entities:
{
"useraccountid": "U12345",
"profiles": [
{
"applicationrole": "RelationshipManager",
"maindatascopetypecd": 858000001,
"organisationalunitno": "10000000",
"ishierachical": 1
},
{
"applicationrole": "CountrySpecialist",
"maindatascopetypecd": 858000002,
"attributetypecd": 858000000,
"attributevalue": "SY",
"isreadonly": 0
}
]
}
Each user can have multiple user provisioning profiles. this data finally needs to be written into custom CRM entities. The "useraccountid" is a lookup to a systemuser (entityreference).
What I already have is e deserializer like this:
public static T JsonDeserialize<T>(string jsonString)
{
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(T));
MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(jsonString));
T obj = (T)ser.ReadObject(ms);
return obj;
}
And classes like this for the parent class:
[DataContract]
class CrmUserProvisioning
{
[DataMember]
public String clm_useraccountid
{
get; set;
}
// will be set on runtime
public DateTime clm_createdon
{
get; set;
}
[DataMember]
public List<CrmProfile> clm_profiles { get; set; } = new List<CrmProfile>();
}
and for the profile(s)
[DataContract]
public class CrmProfile
{
[DataMember]
public Guid clm_userprovisioningid
{
get; set;
}
[DataMember]
public string clm_applicationrole
{
get; set;
}
[DataMember]
public int clm_maindatascopetypecd
{
get; set;
}
[DataMember]
public string clm_organisationalunitno
{
get; set;
}
[DataMember]
public bool clm_ishierachical
{
get; set;
}
[DataMember]
public int clm_applyingtypecd
{
get; set;
}
[DataMember]
public int clm_globalopenaccesscd
{
get; set;
}
[DataMember]
public int clm_attributetypecd
{
get; set;
}
[DataMember]
public string clm_attributevalue
{
get; set;
}
[DataMember]
public bool clm_isreadonly
{
get; set;
}
}
missing fields in the config for the profile will not be deserialized. I'm stuck with the fact on how deserialize this config regarding the fact that the config can contain many profile classes but only one parent class (UserProvisioning).
Could anybody put me in the right direction? Any help is really appreciated.
kind regards
UPDATE 02.03.2018
After having deserialized the Jsion into object(s) I need to store now the objects into the corresponding Microsoft Dynamics CRM entites. The entities and the attributes are named like the object- and the property-names.
I already have the organisation service, etc. I only need to know how to map the objects to a regular crm create or update request.
If somebody could help me out with this, it would be very appreciated.
Use DataContract and DataMember
[DataContract]
class UserProvisioning
{
[DataMember]
public String useraccountid
{
get { return this.useraccountid; }
set { this.useraccountid = value; }
}
// Will be set on runtime
public DateTime createdon
{
get { return this.createdon; }
set { this.createdon = value; }
}
// Must declare this for the child list of Profile
[DataMember]
public List<Profile> profiles {get;set;}=new List<Profile>();
}
In CrmProfile also set DataContract for the class and DataMember for the properties.
Check the docs:
DataContract
DataMember. Only the properties with DataMember will serialize.
You can also set required properties or set not to serialize default values.

Deserialize multiple JSON records in C# [duplicate]

This question already has an answer here:
Deserializing JSON into an object
(1 answer)
Closed 5 years ago.
I have the following string of Json records:
{
"records":[
{
"PK":"1_1_8",
"ID":"8",
"DeviceID":"1",
"RootID":"1",
"CustName":"test1",
"CustSurname":"test2",
"Address":"Nisou 1",
"City":"",
"ZipCode":"",
"PhoneNumber":"45646",
"HomePhoneNumber":"",
"Email":"",
"Notes":"",
"Owner":"1",
"LanguageID":"1",
"LanguagePK":"",
"DeletedFlag":"false",
"created":"2017-10-25 10:15:00",
"modified":"2017-10-25 09:35:43"
},
{
"PK":"1_1_33",
"ID":"33",
"DeviceID":"1",
"RootID":"1",
"CustName":"",
"CustSurname":"",
"Address":"",
"City":"",
"ZipCode":"",
"PhoneNumber":"",
"HomePhoneNumber":"",
"Email":"",
"Notes":"",
"Owner":null,
"LanguageID":"0",
"LanguagePK":"",
"DeletedFlag":"true",
"created":"2017-10-25 10:13:54",
"modified":"2017-10-25 10:13:54"
},
{
"PK":"1_1_16",
"ID":"16",
"DeviceID":"1",
"RootID":"1",
"CustName":"Theodosis",
"CustSurname":"",
"Address":"Dali",
"City":"Nicosia",
"ZipCode":"2540",
"PhoneNumber":"45645",
"HomePhoneNumber":"99123456",
"Email":"theodosis#gmail.com",
"Notes":"",
"Owner":"",
"LanguageID":"1",
"LanguagePK":"",
"DeletedFlag":"false",
"created":"2017-10-25 09:36:22",
"modified":"2017-10-25 09:36:22"
}
]
}
I am using Xamarin PCL in C# trying to parse this string into a list of objects.
I have a Customer class:
public class Customer
{
[PrimaryKey]
public string PK { get; set; }
public int DeviceID { get; set; }
public int ID { get; set; }
public string RootID{ get; set; }
public string CustName { get; set; }
public string CustSurname { get; set; }
public string Address { get; set; }
public string City { get; set; }
public string ZipCode { get; set; }
public string PhoneNumber { get; set; }
public string HomePhoneNumber { get; set; }
public string Email { get; set; }
public string Notes { get; set; }
public bool Owner { get; set; }
public int LanguageID { get; set; }
public string LanguagePK { get; set; }
public bool DeletedFlag { get; set; }
public DateTime created { get; set; }
public DateTime modified { get; set; }
}
I also tried out having a container class with a list of Customer objects.
public class DataContainer
{
public List<Customer> customers { get; set; }
}
I have seen quite a few of examples online on how to parse this into a list or any workable type but nothing seems to be working for me.
I have tried the following (JsonResults holds the string of Json records):
var observation = JsonConvert.DeserializeObject<DataContainer>(JsonResults);
From other posts, I am not able to access JavaScriptSerializer class from my code, perhaps because of the Xamarin PCL Framework I am using.
Any ideas would be very welcome, as I said I do not mind the format I parse the string into, as long as it's workable.
Thank you.
You would have to make the following changes to your code to make this work.
First and most importantly, you don't have a property customers, you have records, so either rename it
public class DataContainer {
public List<Customer> records { get; set; }
}
or add a JsonProperty attribute
[JsonProperty(PropertyName = "records")]
Secondly, your Owner is a bool in C# and a nullable int (int?) in Json. So either change it in your C# class
public int? Owner { get; set; }
or write a converter to do that (e.g. like here)
[JsonConverter(typeof(NullableIntToBooleanConverter))]
public bool Owner { get; set; }
Here is a working .NetFiddle
The JSON string you provided is a JSON object, which contains a single property called records. records property is a List<Customer>. You can not deserialize the given string directly into DataContainer class that you provided because the property names do not match.
In the Class that your provided it is called customers
public class DataContainer {
public List<Customer> customers { get; set; } //records
}
Or please have a look at the attribute for a bit of advanced mapping
[JsonProperty]
JSON you provided is of the form:
{"records":[{Customer},{Customer},{Customer}]}
But Owner property is "1", null or "". Therefore I would suggest redefining Owner as int? (nullable)
Your string shows one object with a property named records that contains a list of other objects. Your code is trying to deserialize this into an object that doesn't have such a property.
Furthermore, the string contains objects with a property Owner that may be missing or have a numeric value. It's definitely not a bool.
You'll have to change Owner to :
public int? Owner { get; set; }
To deserialize the string, you need an object with a records property:
public class DataContainer
{
public Customer[] records { get; set; }
}
var data=JsonConvert.DeserializeObject<DataContainer>(json);
Debug.Assert(data.records.Length == 3);

Dynamic/Expando and JSON

There's a lot of Qs on this, but I need a solution without JSON.Net, etc. - I must use the canned stuff in Asp.Net MVC.
How can I serialize a POCO with a dynamic property - and get all the static properties, too? What I found was the dynamic only, or the static type which is easy.
e.g.
public class ReturnThisClassAsJSON {
public int Id {get; set; }
public string Name { get; set; }
public ContainedClass ContainedContents { get; set; }
}
public class ContainedClass {
public int Order { get; set; }
public string Label { get; set; }
public dynamic DynamicInfo { get; set; }
public List<dynamic> DynamicList { get; set }
}
My own answer:
I replaced the dynamic from the DynamicInfo and DynamicList from the ContainedClass with static types.
With the dynamic, I had 1 of 2 choices. Either serialize the dynamic to a string in its own serialization call using above SO question 5156664. (Which left me with the rest of the class I also wanted serialized and merged with it, thus this question). Or, incur this error:
"A circular reference was detected while serializing an object of type 'System.Reflection .RuntimeModule' ".
when attempting a single serialization call on the ContainedClass.
So, I transferred the dynamics into static-typed classes:
public class ColumnValue
{
public string Name { get; set; }
public string Value { get; set; }
}
public class DynamicRow
{
public List<ColumnValue> ColumnValue { get; set; }
}
and, change ContainedClass to this:
public class ContainedClass
{
public List<ColumnValue> DynamicInfo { get; set; }
public List<DynamicRow> Data { get; set; }
}
And, it serializes using out-of-the-box Asp.Net MVC:
return Json(ReturnThisClassAsJSON, JsonRequestBehaviour.AllowGet);

Unable to parse JSON with empty array/objects

Trying the parse this json file and it seems to be getting stuck on empty objects and arrays.
{"Unexpected character encountered while parsing value: [. Path 'notes'."}
{
"id":null,
"phone":null,
"name":" ",
"email":null,
"address":null,
"assignee":null,
"notes":[
],
"created_at":null,
"items":{
"0":{
"minimized":false,
"sku":{
"partner_id":null,
"type_id":0,
"errors":{
}
}
}
}
}
CLASSES
public class RootObject
{
public string id { get; set; }
public string phone { get; set; }
public string name { get; set; }
public string email { get; set; }
public string address { get; set; }
public string assignee { get; set; }
public string notes { get; set; }
public string created_at { get; set; }
public Items items { get; set; }
}
public class Items
{
public bool minimized { get; set; }
public Sku sku { get; set; }
}
public class Sku
{
public int partner_id { get; set; }
public int type_id { get; set; }
public Errors errors { get; set; }
}
public class Errors
{
}
The issue appears to be with handling the Notes and Errors property, I've tried to use the following settings as per a few other SO posts but nothing has worked and I am not sure how to get this to deserialize into an object.
RootObject o = JsonConvert.DeserializeObject<RootObject>(json, new JsonSerializerSettings
{
MissingMemberHandling = MissingMemberHandling.Ignore,
NullValueHandling = NullValueHandling.Ignore
});
Perhaps someone can help me out because it seems to me JSON.net should be able to handle these properties?
Here's a working dotNetFiddle: https://dotnetfiddle.net/Lnkojw
Here's the output.
NOTE: I put in sample values in your json for your null values so you can see that it is working)
Your class definitions (autogenerated from http://json2csharp.com/ ) need to be modified as shown below.
public class RootObject
{
public string id { get; set; }
public string phone { get; set; }
public string name { get; set; }
public string email { get; set; }
public string address { get; set; }
public string assignee { get; set; }
public List<string> notes { get; set; }
public string created_at { get; set; }
public Dictionary<int,Item> items { get; set; }
}
public class Item
{
public bool minimized { get; set; }
public Sku sku { get; set; }
}
public class Sku
{
public int partner_id { get; set; }
public int type_id { get; set; }
[JsonIgnore]
public object errors { get; set; }
}
Since you have stated in comments that Errors will always be empty, I removed that redundant Errors class you had, with no properties or members. I also set the errors member in the Sku class to be object type in case you get values in future. Finally I set this errors property to [JsonIgnore] so json.net will ignore it for serialization / deserialization
Also Items appears to be Dictionary with an int for key and an Item for Value. So I have changed the definition there also.
Here's the code that deserializes and prints out the values.
using System;
using Newtonsoft.Json;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
string json = #"{
""id"":1,
""phone"":""415-000-1234"",
""name"":"" "",
""email"":null,
""address"":null,
""assignee"":null,
""notes"":[
],
""created_at"":null,
""items"":{
""0"":{
""minimized"":false,
""sku"":{
""partner_id"":21,
""type_id"":44,
""errors"":{
}
}
}
}
}";
Console.WriteLine("Deserializing json...");
RootObject o = JsonConvert.DeserializeObject<RootObject>(json, new JsonSerializerSettings
{
MissingMemberHandling = MissingMemberHandling.Ignore,
NullValueHandling = NullValueHandling.Ignore
});
Console.WriteLine("Success!");
Console.WriteLine("id #: {0}",o.id);
Console.WriteLine("phone #: {0}",o.phone);
foreach (var item in o.items)
{
Console.WriteLine(" Item #: {0}",item.Key);
if (item.Value != null)
{
Console.WriteLine(" SKU: partner_id: {0}",item.Value.sku.partner_id);
Console.WriteLine(" SKU: type_id: {0}",item.Value.sku.type_id);
}
}
}
}
And once again, here's the output. You can see the json values properly deserialized.
PRO TIPS:
Use a sample json that has values for as many fields as possible, so that http://json2csharp.com/ can generate the proper classes
Whenever you have a nameless property like the items in your json,you will likely need to inspect what http://json2csharp.com/ generated and modify it to be a Dictionary or a NameValuePair or KeyValuePair or something else. It is on a case by case basis. In other words, for 99% of well designed json, you can "plug and play" with http://json2csharp.com/ , for the remaining 1% you will have to customize the classes generated, or your serialization code or both.
The issue seems to be that in your auto-properties for RootObject, you have the notes property listed as a string rather than string[] - if notes are indeed an array of strings, your JSON snippet doesn't show that.
You should also be able to use a List<string> for notes, if you would prefer that.

Categories