Custom mapping with Json.NET - c#

I'm trying to map JSON that looks like
"ids": {
"id": {
"#value":"6763754764235874140"
}
}
And I'd like to map it onto a couple of classes that look like
class Property
{
public Ids Ids { get; set; }
}
class Ids
{
public string Id { get; set; }
}
So basically I want to stuff the value of ids/id/#value from the JSON document into Ids.Id in the class architecture. From browsing the documentation, I thought I could use something like
[JsonProperty(ItemConverterType=typeof(IdConverter))]
public string Id { get; set; }
and provide a custom JsonConverter subclass named IdConverter. When I do, though, my IdConverter.ReadJson never gets called. What am I doing wrong?

Looks like the answer was that ItemConverterType is for converting items in an array. Double-annotating the property with JsonProperty and JsonConverter attributes works:
[JsonProperty(PropertyName="id")]
[JsonConverter(typeof(IdConverter))]
public string Id { get; set; }

Related

Keeping consistent property names in dynamic objects?

So, I'm not sure where my head is at today but I can't wrap my head around a decent solution to this issue and I was hoping you guys can help.
We are building a .NET Core / EF Core / SQL application that hands a base record type, and then we layer on metadata objects on top of that base record to enrich the datasets. So my base class looks something like this:
public class Record
{
[NotNull]
public Guid Id { get; set }
[CanBeNull]
public virtual string InvoiceNum { get; set; }
[CanBeNull]
public virtual string Notes { get; set; }
//Enum that defines the extra properties
public virtual RecordType RecordType { get; set; }
public Dictionary<string,object> ExtraProperties { get; set; }
}
and my MetaData class looks something like this:
public class MetaData
{
[NotNull]
public Guid Id { get; set; }
[NotNull]
public Guid RecordId { get; set; }
[NotNull]
public virtual string Description { get; set; }
//Enum that defines the extra properties
public virtual MetaDataTypes MetaDataType { get; set; }
public Dictionary<string,object> ExtraProperties { get; set; }
}
I am using the ExtraProperties field in each object to store a JSON encoded string of additional data for each type, determined by the Enum values noted in the objects above.
What I am trying to find out, is how do I consistently (and efficiently) define those metadata fields on an incoming JSON without needing to hard code each individual type?
For example, I was thinking the JSON for a complete incoming record could be something like this:
{
"Record" : {
"InvoiceNum":"12345",
"Notes":"Notes go here",
"MetaData" : [
{
"MetaDataType" : "Address",
"Properties" :
{
"Address01":"123 ABC Ave",
"City":"New York City",
"Country":"US"
}
},
{
"MetaDataType":"ClientContact",
"Properties" :
{
"FirstName":"John",
"LastName":"Doe",
"Email":"jdoe#example.com",
"Phone":"8675309"
}
}]
}
}
But I want the "Properties" objects to have consistent values based on what the MetaDataType object is defined as and I only want to store the properties for that particular object type in the database attached to that object (there are, across all metadata types, about a hundred unique values).
Hopefully that makes sense, like I said earlier my brain is fried a little today so maybe I am missing something really obvious, but I would like to get your thoughts.

Newtonsoft JSON rename the property name depends on another property

I have a class with the following fields.
public class Payment
{
public string type { get; set; }
public string issuer { get; set; }
public string identifier { get; set; }
public float price { get; set; }
}
The "type" field can be "Giftcard" or "Creditcard".
I want to serialize it depends on the "type" field.
{
"type": "Giftcard",
"giftcard_no": "111111111111",
"giftcard_price": 100
}
{
"type": "Creditcard",
"issuer": "AMEX",
"last_4_digits": "1000",
"creditcard_price": 100
}
As you can see, the field names are different depends on the "type" field.
And "issuer" field is ignored in case of Giftcard.
I've found similar questions, but couldn't find the correct answer.
I will be appreciated for any help.
Thank you.
It sounds to me like what you want is different subclasses, using type to determine which one to use when deserializing. I've found that the JsonSubTypes package (GitHub repo) works very well for this.
You'd have something like this:
[JsonConverter(typeof(JsonSubtypes), "type")]
[JsonSubtypes.KnownSubType(typeof(GiftCard), "Giftcard")]
[JsonSubtypes.KnownSubType(typeof(CreditCard), "Creditcard")]
public class Payment
{
[JsonProperty("type")]
public virtual string Type { get; }
}
public class CreditCard : Payment
{
public override string Type => "Creditcard";
[JsonProperty("issuer")
public string Issuer { get; set; }
// Etc, other properties
}
public class GiftCard : Payment
{
public override string Type => "Giftcard";
[JsonProperty("giftcard_no")]
public string GiftCardNumber { get; set; }
// Etc, other properties
}
There are various different options for exactly how you register things - the README on the GitHub repo gives concrete examples, which is really useful.
You'd then deserialize to Payment, but the returned value would be a reference to an instance of GiftCard or CreditCard.

How to deserialize JSON with spaces in the attribute names?

I have a string array that I want to deserialize. Essentially, it is just a list of objects. Note that the attributes have spaces in the names:
[ { \"Event Name\": \"Hurricane Irma PR\", \"Storm Start (LST)\": \"2017-08-30\", \"Storm End (LST)\": \"2017-09-13\", \"Grid Cell Number\": 16412, \"Grid Cell State\": \"PR\", \"Grid Cell Name\": \"Grid26_0\", ...
I created a public class to template the string based on specific attributes that I want ( I don't want all the data) but I am not sure how to handle for the spaces in the names of the attributes that I want.
public class New_Events_Dataset
{
public string EventName { get; set; }
public string StormStart { get; set; }
public string StormEnd { get; set; }
public string GridCellState { get; set; }
public string GridCellName { get; set; }
public string USGSGageSiteNo { get; set; }
public string ReturnPeriodatGridCell { get; set; }
}
When I apply the deserializer with my class New_Events_Dataset like this:
var jsonResponse = returnJson.Deserialize<List<New_Events_Dataset>>(strresult);
string json = new JavaScriptSerializer().Serialize(jsonResponse);
return json;
I end up returning something like this. What am I doing wrong?
[{"EventName":null,"StormStart":null,"StormEnd":null,"GridCellState":null,"GridCellName":null,"USGSGageSiteNo":null,"ReturnPeriodatGridCell":null}
Unfortunately keys must match exactly each other.
One of the best ways to solve your problem is to define JsonProperty attribute for each property to get Deserialized object correctly. You can specify property's json key name with it.
You can take a look to this question and it's answer for better understanding:
An example of JsonProperty
Edit:
As in comments mentioned, because you are using JavaScriptSerializer JsonPropertyAttribute doesn't work in this situation.
But you can use it by adding Newtonsoft.Json Nuget Package and using it's deserilizer this way:
JsonConvert.DeserializeObject<AzureResourceData>(jsonString);

Deserializing Generic that implements Interface with a List<T> of Interfaces .NET C#

Scenario
I have some XML come down from a service that I want to deserialize.
Depending on what is returned from the service, the XML can vary slightly (with the element names); but the XML always follows a common structure.
Here is a sample of what the XML might look like:
<ATemplate>
<Name>SomeTemplate</Name>
<TemplateItems>
<ATemplateItem>
<Name>SomeTemplateItem</Name>
<TemplateFields>
<ATemplateField>
<Name>SomeTemplateField</Name>
<Colour>Blue</Colour>
</ATemplateField>
... more template fields
</TemplateFields>
</ATemplateItem>
... more template items
</TemplateItems>
</ATemplate>
Using the above XML as an example, I have created a ATemplate class that will deserialize nicely from the XML, using the ATemplateItem and ATemplateField classes accordingly:
public class ATemplate
{
public string Name { get; set; }
public List<ATemplateItem> TemplateItems { get; set; }
}
public class ATemplateItem
{
public string Name { get; set; }
public List<ATemplateField> TemplateFields { get; set; }
}
public class ATemplateField
{
public string Name { get; set; }
public string Colour { get; set; }
}
I use this code to deserialize:
ATemplate template;
using (TextReader reader = new StringReader(xmlString))
{
template = (ATemplate)new XmlSerializer(typeof(ATemplate)).Deserialize(reader);
}
All good, so far.
Curveball
The same scenario might occur where the XML contains BTemplate, BTemplateItems and BTemplateFields; still following the structure as above.
So I created other classes for this situation:
public class BTemplate { ... }
public class BTemplateItem { ... }
public class BTemplateField { ... }
And made the relevant classes inherit respectively from ITemplate, ITemplateItem and ITemplateField I created, also:
Interfaces
public class ITemplate
{
public string Name { get; set; }
public List<ITemplateItem> TemplateItems { get; set; }
}
public class ITemplateItem
{
public string Name { get; set; }
public List<ITemplateField> TemplateFields { get; set; }
}
public class ITemplateField
{
public string Name { get; set; }
public string Colour { get; set; }
}
This is so I can then create one function, which is able to loop through the ITemplateItems and their ITemplateFields and perform some cool stuff:
public void Foo(ITemplate template)
{
foreach (var item in template.TemplateItems)
{
// do cool stuff
foreach (var field in item.TemplateFields)
{
// do more cool stuff
}
}
}
Some things to note:
In the object that contains the XML, I know what "type" the XML contains - given an Enum I use to identify
I then use a switch statement to run different methods, depending on the said "type"
Generic Method?
Now, rather than deserializing the XML differently in each of those method cases, I would like to use a Generic method to deserialize.
So I created one, like this:
public ITemplate DeserializeTemplate<T>(string xmlString) where T : ITemplate
{
using (TextReader reader = new StringReader(xmlString))
{
return (T)new XmlSerializer(typeof(T)).Deserialize(reader);
}
}
And call it from within the specific methods like so:
var template = DeserializeTemplate<ATemplate>(xmlString);
Then, I can use the ITemplate that it returns, and pass it to Foo(ITemplate template) to go and perform some magic and wizardry.
But...
No compilation errors, as yet - however I get a RunTime error, because it cannot deserialize an Interface.
I gather this is because it's trying to then deserialize the ITemplate's TemplateItems as ITemplateItems.
Can I do the above?
My question is:
How can I get around this issue?
Can I use this Generic deserialize method?
Will I need to treat each one differently in the separate methods?
Will I need to make the Interface generic also, with the types to expect?
I'm banging my head against the desk, so I really hope you lovely SO people can help.
As always, your comments, answers and suggestions are much appreciated :)

Bind string to class in controller method

I would like to bind submission of JSON like this
{
"id": 1,
"name": "bob",
"phone": "(425) 882-8080"
}
to...
class Model
{
public int Id { get; set; }
public string Name { get; set; }
public PhoneNumber Phone { get; set; }
}
where the PhoneNumber class is able to bind to the phone string in the JSON.
The idea was the use a json.net custom converter like:
class Model
{
public int Id { get; set; }
public string Name { get; set; }
[JsonConverter(typeof(PhoneNumberCoverter))]
public PhoneNumber Phone { get; set; }
}
The problem is that MVC is not even trying to use the ReadJson method. Model.Phone == null.
I have tried a few things. Hoping that if I had implicit operative overrides to and from string for the PhoneNumber class, it may just do it automatically. Nope.
What is the correct way to customize model binding for this scenario?
I think you expect that when your action method like
public ActionResult Post(Model myModel)
{
// you expect that myModel should has used JSonConverter and provide you data.
}
Above thing will not work as you expected and reason for this JsonConvertor attribute is specific Json.net and not MVC attribute so DefaultModelBinder will not consider that attribute to bind your data.
Possible correct and simple way to do (But not strongly type)
public ActionResult Post(string jsonData)
{
// Now here use Json.net JsonConvert to deserialize string to object of Type Model.
}
Another solution to is to build your own custom modelbinder and use Json.net inside that.

Categories

Resources