Using JsonConvert.DeserializeObject to deserialize Json to a C# POCO class - c#

Here is my simple User POCO class:
/// <summary>
/// The User class represents a Coderwall User.
/// </summary>
public class User
{
/// <summary>
/// A User's username. eg: "sergiotapia, mrkibbles, matumbo"
/// </summary>
public string Username { get; set; }
/// <summary>
/// A User's name. eg: "Sergio Tapia, John Cosack, Lucy McMillan"
/// </summary>
public string Name { get; set; }
/// <summary>
/// A User's location. eh: "Bolivia, USA, France, Italy"
/// </summary>
public string Location { get; set; }
public int Endorsements { get; set; } //Todo.
public string Team { get; set; } //Todo.
/// <summary>
/// A collection of the User's linked accounts.
/// </summary>
public List<Account> Accounts { get; set; }
/// <summary>
/// A collection of the User's awarded badges.
/// </summary>
public List<Badge> Badges { get; set; }
}
And the method I'm using to deserialize a JSON response into a User object (this actual JSON call is here):
private User LoadUserFromJson(string response)
{
var outObject = JsonConvert.DeserializeObject<User>(response);
return outObject;
}
This fires an exception:
Cannot deserialize the current JSON object (e.g. {"name":"value"})
into type
'System.Collections.Generic.List`1[CoderwallDotNet.Api.Models.Account]'
because the type requires a JSON array (e.g. [1,2,3]) to deserialize
correctly.
To fix this error either change the JSON to a JSON array
(e.g. [1,2,3]) or change the deserialized type so that it is a normal
.NET type (e.g. not a primitive type like integer, not a collection
type like an array or List) that can be deserialized from a JSON
object. JsonObjectAttribute can also be added to the type to force it
to deserialize from a JSON object. Path 'accounts.github', line 1,
position 129.
Having never worked with this DeserializeObject method before, I'm kind of stuck here.
I've made sure that the property names in the POCO class are the same as the names in the JSON response.
What can I try to deserialize JSON into this POCO class?

Here is a working example.
Keypoints are:
Declaration of Accounts
Use of JsonProperty attribute
.
using (WebClient wc = new WebClient())
{
var json = wc.DownloadString("http://coderwall.com/mdeiters.json");
var user = JsonConvert.DeserializeObject<User>(json);
}
-
public class User
{
/// <summary>
/// A User's username. eg: "sergiotapia, mrkibbles, matumbo"
/// </summary>
[JsonProperty("username")]
public string Username { get; set; }
/// <summary>
/// A User's name. eg: "Sergio Tapia, John Cosack, Lucy McMillan"
/// </summary>
[JsonProperty("name")]
public string Name { get; set; }
/// <summary>
/// A User's location. eh: "Bolivia, USA, France, Italy"
/// </summary>
[JsonProperty("location")]
public string Location { get; set; }
[JsonProperty("endorsements")]
public int Endorsements { get; set; } //Todo.
[JsonProperty("team")]
public string Team { get; set; } //Todo.
/// <summary>
/// A collection of the User's linked accounts.
/// </summary>
[JsonProperty("accounts")]
public Account Accounts { get; set; }
/// <summary>
/// A collection of the User's awarded badges.
/// </summary>
[JsonProperty("badges")]
public List<Badge> Badges { get; set; }
}
public class Account
{
public string github;
}
public class Badge
{
[JsonProperty("name")]
public string Name;
[JsonProperty("description")]
public string Description;
[JsonProperty("created")]
public string Created;
[JsonProperty("badge")]
public string BadgeUrl;
}

Another, and more streamlined, approach to deserializing a camel-cased JSON string to a pascal-cased POCO object is to use the CamelCasePropertyNamesContractResolver.
It's part of the Newtonsoft.Json.Serialization namespace. This approach assumes that the only difference between the JSON object and the POCO lies in the casing of the property names. If the property names are spelled differently, then you'll need to resort to using JsonProperty attributes to map property names.
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
. . .
private User LoadUserFromJson(string response)
{
JsonSerializerSettings serSettings = new JsonSerializerSettings();
serSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
User outObject = JsonConvert.DeserializeObject<User>(jsonValue, serSettings);
return outObject;
}

The accounts property is defined like this:
"accounts":{"github":"sergiotapia"}
Your POCO states this:
public List<Account> Accounts { get; set; }
Try using this Json:
"accounts":[{"github":"sergiotapia"}]
An array of items (which is going to be mapped to the list) is always enclosed in square brackets.
Edit: The Account Poco will be something like this:
class Account {
public string github { get; set; }
}
and maybe other properties.
Edit 2: To not have an array use the property as follows:
public Account Accounts { get; set; }
with something like the sample class I've posted in the first edit.

You could create a JsonConverter. See here for an example thats similar to your question.

Along the lines of the accepted answer, if you have a JSON text sample you can plug it in to this converter, select your options and generate the C# code.
If you don't know the type at runtime, this topic looks like it would fit.
dynamically deserialize json into any object passed in. c#

to fix this error either change the JSON to a JSON array (e.g. [1,2,3]) or change the
deserialized type so that it is a normal .NET type (e.g. not a primitive type like
integer, not a collection type like an array or List) that can be deserialized from a
JSON object.`
The whole message indicates that it is possible to serialize to a List object, but the input must be a JSON list.
This means that your JSON must contain
"accounts" : [{<AccountObjectData}, {<AccountObjectData>}...],
Where AccountObject data is JSON representing your Account object or your Badge object
What it seems to be getting currently is
"accounts":{"github":"sergiotapia"}
Where accounts is a JSON object (denoted by curly braces), not an array of JSON objects (arrays are denoted by brackets), which is what you want. Try
"accounts" : [{"github":"sergiotapia"}]

That's not exactly what I had in mind. What do you do if you have a generic type to only be known at runtime?
public MyDTO toObject() {
try {
var methodInfo = MethodBase.GetCurrentMethod();
if (methodInfo.DeclaringType != null) {
var fullName = methodInfo.DeclaringType.FullName + "." + this.dtoName;
Type type = Type.GetType(fullName);
if (type != null) {
var obj = JsonConvert.DeserializeObject(payload);
//var obj = JsonConvert.DeserializeObject<type.MemberType.GetType()>(payload); // <--- type ?????
...
}
}
// Example for java.. Convert this to C#
return JSONUtil.fromJSON(payload, Class.forName(dtoName, false, getClass().getClassLoader()));
} catch (Exception ex) {
throw new ReflectInsightException(MethodBase.GetCurrentMethod().Name, ex);
}
}

May be late but using QuickType is the easiest way to do that:
https://app.quicktype.io/

For anyone having this problem i was not seeing the json value properly. https://jsonutils.com/ there you can check the classes that should be generated and return ONLY ONE of those classes once you read the json in your code.
For example i needed a booklist object so my code should only read one
res = await response.Content.ReadAsAsync<BookList>();
Where booklist looks something like
public class BookList
{
[JsonProperty("data")]
public IList<Datum> Data { get; set; }
}
And in that list have smaller book clasess that the converter named Datum (just books)
public class Datum
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("isbn")]
public string Isbn { get; set; }
}
Again, if you have doubts https://jsonutils.com/

Related

Attributes on Field in Class

I want to get a list of fields with an Attribute Sync.Field on each of the field in the class. The field can / cannot have the attribute of Sync.Field
I have been trying the following, but having trouble getting the custom attribute for each field.
FieldInfo[] fiClass = typClass.GetFields();
FieldInfo[] lst = fiClass
.Where(c => c.CustomAttribute().GetType() == typeOf(Sync.Field))
.ToList();
I have a generic collection class, which uses a data class to match an SNMP table with data class fields. Like JsonProperty matches deserialised values to properties. In the same way I define a SNMPPropertyAttribute. The attribute itself is
[AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = false)]
sealed class SNMPPropertyAttribute : Attribute
{
public SNMPPropertyAttribute(string propertyOID) => PropertyOID = new ObjectIdentifier(propertyOID);
public ObjectIdentifier PropertyOID { get; }
}
When in the table constructor, I'm making a dictionary of data fiels and their OIDs from the attribute:
public SNMPTableEntity()
{
snmpPoperties = new Dictionary<ObjectIdentifier, PropertyInfo>();
foreach (PropertyInfo myProperty in GetType().GetProperties(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public))
{
CustomAttributeData snmpAttribure = myProperty.CustomAttributes.Where(x => x.AttributeType == typeof(SNMPPropertyAttribute)).FirstOrDefault();
if (snmpAttribure != null)
snmpPoperties.Add(new ObjectIdentifier((string)snmpAttribure.ConstructorArguments[0].Value), myProperty);
}
}
It looks similar to what are you trying to acheive, so hopefully it helps. But the difference, is that I'm using properties, not fields. Not sure if it makes a big difference, but...
There is an example of using:
public class InterfaceTableEntity : SNMPTableEntity
{
/// <summary>
/// A unique value for each interface. Its value ranges between 1 and the value of ifNumber. The value for each interface must remain constant at least from one re-initialization of the entity's network management system to the next re- initialization.
/// </summary>
[SNMPProperty("1.3.6.1.2.1.2.2.1.1")]
protected Integer32 ifIndex { get; set; }
/// <summary>
/// A textual string containing information about the interface. This string should include the name of the manufacturer, the product name and the version of the hardware interface.
/// </summary>
[SNMPProperty("1.3.6.1.2.1.2.2.1.2")]
protected OctetString ifDescr { get; set; }
/// <summary>
/// The type of interface, distinguished according to the physical/link protocol(s) immediately `below' the network layer in the protocol stack.
/// </summary>
[SNMPProperty("1.3.6.1.2.1.2.2.1.3")]
protected Integer32 ifType { get; set; }
}
If you have the FieldInfo, you can get an instance of its attribute using this code:
var attr = fieldInfo.GetCustomAttributes().OfType<Sync.FieldAttribute>().SingleOrDefault();
See my example on DotNetFiddle.

Deserialize specific enum into system.enum in Json.Net

I have a fairly generic 'rule' class that I am using to drive the behavior of an analysis engine I'm writing:
public class Rule
{
/// <summary>
/// The general rule type.
/// </summary>
public RuleType RuleType { get; set; }
/// <summary>
/// The human-readable description of the rule.
/// </summary>
public string RuleDescription { get; set; }
/// <summary>
/// The integer magnitude of the rule, if applicable.
/// </summary>
public int? RuleInt { get; set; }
/// <summary>
/// The boolean sign associated with the rule, if applicable.
/// </summary>
public bool? RuleBool { get; set; }
/// <summary>
/// The enum flag associated with the rule, if applicable. CAN be null.
/// </summary>
public System.Enum RuleFlagEnum { get; set; }
/// <summary>
/// A dumping ground for any other random crap I've failed to account for at this point in time.
/// </summary>
public object RuleObject { get; set; }
}
RuleType is a specific enum, like so:
public enum RuleType
{
Invalid,
ModifyDifficulty,
StrengthChange,
ColorChange,
SignChange
}
Using Json.NET, that both serializes and deserializes just fine.
RuleEnum, however, is giving me problems. Whether using the default enum serialization or the string enum serialization, the specific type of enum is not provided. As such, during deserialization, I am left with System.Enum and a string value, which is wholly unhelpful.
This is an example of the serialization, to show what I'm talking about:
{
"RuleType": "SignChange",
"RuleDescription": "Strength 1 Inversion Gate",
"RuleInt": 1,
"RuleFlagEnum": "Negative"
}
RuleFlagEnum, in this case, is referring to the enum:
public enum SignChange
{
Zero,
Positive,
Negative
}
I have tried using all of the TypeNameHandling options inside Json.NET. They only put type hinting on the objects, which doesn't help with RuleFlagEnum since it is technically a primitive.
I would really, really like to keep the enum at System.Enum so we can load any arbitrary enum in for later interpretation by the rule type, so the entire thing is more expandable. Is this possible?
The difficulty here is that System.Enum is an abstract class, so it is impossible to deserialize a value of unknown concrete type as such a type. Rather, one needs to have the specific type information in the JSON somewhere, however Json.NET will serialize an enum as a string or an integer (depending upon whether a StringEnumConverter is applied) -- but not an as an object, thus leaving no opportunity for a polymorphic "$type" property to be added.
The solution is, when serializing, to serialize a generic wrapper class that can convey the concrete type information:
public abstract class TypeWrapper
{
protected TypeWrapper() { }
[JsonIgnore]
public abstract object ObjectValue { get; }
public static TypeWrapper CreateWrapper<T>(T value)
{
if (value == null)
return new TypeWrapper<T>();
var type = value.GetType();
if (type == typeof(T))
return new TypeWrapper<T>(value);
// Return actual type of subclass
return (TypeWrapper)Activator.CreateInstance(typeof(TypeWrapper<>).MakeGenericType(type), value);
}
}
public sealed class TypeWrapper<T> : TypeWrapper
{
public TypeWrapper() : base() { }
public TypeWrapper(T value)
: base()
{
this.Value = value;
}
public override object ObjectValue { get { return Value; } }
public T Value { get; set; }
}
Then use serialize the wrapper when serializing your class:
/// <summary>
/// The enum flag associated with the rule, if applicable. CAN be null.
/// </summary>
[JsonIgnore]
public System.Enum RuleFlagEnum { get; set; }
[JsonProperty("RuleFlagEnum", TypeNameHandling = TypeNameHandling.All)]
TypeWrapper RuleFlagEnumValue
{
get
{
return RuleFlagEnum == null ? null : TypeWrapper.CreateWrapper(RuleFlagEnum);
}
set
{
if (value == null || value.ObjectValue == null)
RuleFlagEnum = null;
else
RuleFlagEnum = (Enum)value.ObjectValue;
}
}
This produces JSON like the following:
{
"RuleType": "ModifyDifficulty",
"RuleFlagEnum": {
"$type": "Question31351262.TypeWrapper`1[[Question31351262.MyEnum, MyApp]], MyApp",
"Value": "Two, Three"
},
}

Best way to periodically query a server and return a specific value in addition to another value

Maybe the title is not good enough, I know. Here is my propblem.
I'm currently writing a class library for a program which handles the new version checks and downloads of the patches.
A dedicated updating framework is no option for me because they mostly are too bloated. I really only want to query the server, check for a new version and if there is a new version download the patch.
In my current setup I have a class which calls the server that returns a json encoded text document (for now, we are going to switch toWebApi2 later). This text document is then deserialized by JSON.NET into a list of the following c# object.
/// <summary>Represents a patch.</summary>
/// ToDo: Implement ICompareable and IEquatable
public class Patch
{
/// <summary>Initializes a new instance of the <see cref="Patch"/> class.</summary>
/// <param name="version">The version of the patch.</param>
[JsonConstructor]
public Patch(string version)
: this(new Version(version))
{
}
/// <summary>Initializes a new instance of the <see cref="Patch"/> class.</summary>
/// <param name="version">The version of the patch as string.</param>
public Patch(Version version)
{
this.Version = version;
}
/// <summary>Gets the version of the patch.</summary>
[JsonConverter(typeof(VersionConverter))]
public Version Version { get; private set; }
/// <summary>Gets or sets the size of the patch.</summary>
public long Length { get; set; }
/// <summary>Gets or sets the primary download uri.</summary>
public Uri DownloadUri { get; set; }
/// <summary>Gets or sets the fallback download uri.</summary>
public Uri FallbackUri { get; set; }
}
After deserialization I iterate through said list and check if a new version is available. If there is a new version available the method returns with true. If there is no new version available the method returns with false.
Now I'm puzzled what is the best way to return the version object itself. I could do this with an out parameter, but I'm not sure if this is the best way. Especially since the checking method has to rerun until it returns false (meaning the program is at the latest version).
Could events be a possibility? How about a tuple? Or am I missing something completely?
//edit: For those who are interested, here is the method which I'm talking about.
public bool CheckForNewVersion(out Patch newPatch)
{
string versionFileContents = this.GetVersionFile();
List<Patch> patches = this.Deserialize(versionFileContents);
foreach (Patch patch in patches)
{
if (patch.Version > this.CurrentVersion)
{
newPatch = patch;
return true;
}
}
newPatch = null;
return false;
}
You could change the signature of the method to something like this
public IEnumerable<Patch> GetUpdates(string currentClientVersion)
{
var updates = new List<Patch>();
if (currentClientVersion != LatestVersion)
{
updates = GetRequiredUpdates(currentClientVersion);
}
return updates;
}
This would give you a list of all the updates to apply, or an empty list if the client has the latest version installed.
Unless i'm misinterpreting the question, I would prefer objects to encapsulate the data required and returned. This will allow you to add any new functionality in the future (as this is an API).
//What patches are available?
public class PatchCheckOutcome {
public PatchCheckOutcome(bool newPatchAvailble, List<Patch> availablePatches) {
NewPatchAvailable = newPatchAvailable;
AvailablePatches = availablePatches ?? New List<Patch>();
}
public bool NewPatchAvailable { get; private set; }
public List<Patch> AvailablePatches { get; private set; }
}
//Information required to check what patches are available
public class PatchCheckInformation {
public string CurrentVersion { get; set; }
}
//API method
public PatchCheckOutcome GetAvailablePatches(PatchCheckInformation info);

How to flatten class containing an IEnumerable of a related class

I have the following class:
public class OrgAlertList
{
public string CustMailName { get; set; }
/// <summary>
/// Gets all the alerts that apply to this customer.
/// </summary>
public virtual IEnumerable<OrgAlertSummary> Alerts { get; set; }
}
That contains a property (named Alerts) that is an IEnumerable of my second class:
public class OrgAlertSummary
{
/// <summary>
/// Message detailing the alert for the user.
/// </summary>
public string Message { get; set; }
/// <summary>
/// Message detailing how to fix alert.
/// </summary>
public string ActionMessage { get; set; }
}
The Alerts property in the OrgAlertList can contain zero to many OrgAlertSummary items.
I need to write a Lambda expression, or use a LINQ query, to flatten the classes into a new type that has the CustMailName, Message, and ActionMessage in it for each OrgAlertList item where the Alerts property contains at least one OrgAlertSummary item. Can anyone help with this?
Try this: (untested)
var q = from orgAlert in myOrgAlertList
from orgAlertSummary in orgAlert.Alerts
select new { orgAlert.CustomMailName,
orgAlertSummary.Message,
orgAlertSummary.ActionMessage};
Assuming myOrgAlertList is some sort of IEnumerable<OrgAlertList>
This query will create an anonymous type with fields named CustomMailName, Message, and ActionMessage. If you intend to export this resulting list to other modules, it's recommended to define your own class and create it in the select instead of using an anonymous type.

How to serialize a class with a property of type object filled with an array

After searching 99% of the net I am still stuck on the following matter. I have a web service which must comply to a wsdl that a partner company supplied. Calling a method of this service results in a (complex) class. Unfortunately a serialization error is raised when the service is called.
I have pinpointed the issue but cannot think of (and find) a solution to it. Because I'm dependant on the wsdl which was supplied, I cannot change the complex class structure. Hope anyone knows what I am missing. Here is example code to reproduce my issue:
[System.SerializableAttribute()]
public class MyObject
{
public int Id { get; set; }
public object Item { get; set; } // <---- Note type *object* here
}
[System.SerializableAttribute()]
public class MyItem
{
public int Id { get; set; }
public string Name { get; set; }
}
[TestClass]
public class SerializationTest
{
[TestMethod]
public void Serializing()
{
MyObject myObject = new MyObject { Id = 1 };
myObject.Item = new MyItem[] { new MyItem { Id = 1, Name = "Test" } };
string serializedString = SerializeObjectToXmlString(myObject, new []{ typeof(MyItem)});
Assert.IsFalse(String.IsNullOrWhiteSpace(serializedString));
}
/// <summary>
/// This method serializes objects to an XML string using the XmlSerializer
/// </summary>
private static string SerializeObjectToXmlString(object theObject, Type[] types)
{
using (var oStream = new System.IO.MemoryStream())
{
var oSerializer = new System.Xml.Serialization.XmlSerializer(theObject.GetType(), types);
oSerializer.Serialize(oStream, theObject); // <- Here the error is raised
return System.Text.Encoding.Default.GetString(oStream.ToArray());
}
}
}
In the Try/Catch an error is raised after calling method Serialize(). Details of this error are:
InvalidOperationException was unhandled by user code
- There was an error generating the XML document.
The type MyItem[] may not be used in this context.
My development context consists of Visual Studio 2010, .Net Framework 3.5.
Edit #1: Added Serialization attributes but the error remaines
It appears that you cannot add an array of types to an object and serialize it. The solution was to create a container class which - like the name says - contains the array. This way you can assign the container class to the object and serialize it all.
In addition to my case, I was mislead by the object model created by the wsdl.exe utility, since the container class is only a technical solution to add an array to an object. This container class was also created so everything was already there to use. Only after trying out my custom container class I noticed the already created container class. Lost a lot of time on this matter unfortunately...
You should mark you classes as
[Serializable]
public class MyObject
{
public int Id { get; set; }
public MyItem[] Item { get; set; } // <---- Note type *object* here
}
[Serializable]
public class MyItem
{
public int Id { get; set; }
public string Name { get; set; }
}
Serialize uknown object (Item of MyObject class) you will need to do manually by implementing proper interfaces:
ISerializable and IDeserializationCallback, botha added to MyObject class.
This is an old question, but I had the same problem and found a different solution, so I thought I'd share in case it helps someone else.
I found that I could add attributes to allow arrays of specific types. For the problem above, the MyObject class could be edited as below:
[System.SerializableAttribute()]
public class MyObject
{
public int Id { get; set; }
[XmlElement(Type = typeof(object), ElementName = "Item"), //added
XmlElement(Type = typeof(MyItem[]), ElementName = "Item_asArrayOfMyItem")] //added
public object Item { get; set; } // <---- Note type *object* here
}
Anything that serialized before will still look the same, but now MyObject can be serialized even when Item has type MyItem[], as in the question's test case.

Categories