two different values in third party JSON API, making Newtonsoft break - c#

Hi I'm hoping you can help.
I'm working on converting Stocks data from a third party API to a c# data type, but the problem is, the JSON objects have multiple values.
For example:
"sharesShortPriorMonth":{"raw":4680878,"fmt":"4.68M","longFmt":"4,680,878"}
the one I want is the fmt value (4.68M), and I'm trying to put it into the C# field public string sharesShortPriorMonth { get; set; }
My question is, when I Deserialize from a JSON string, it's expecting something more like "sharesShortPriorMonth": "4.68M"
How can I tell Json.NET to take that value?
I've already checked the documentation, the JSONProperty Attribute isn't what i want, because that maps different names. not child values.
Thanks in advance.

Personally, I would choose the solution of #Serge (but the sharesShortPriorMonthJo property should be private in my opinion because not necessary from the outside, but rather confusing for the client). If you want to keep your class as clean as possible, then you could use a custom JsonConverter, as pointed out by #GuruStron . However, the complexity is just moved in another class, the JsonConverter precisely.
The simplest (?) JsonConverter you could use in your case is:
public class CustomConverter : JsonConverter<string>
{
public override void WriteJson(JsonWriter writer, string value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override string ReadJson(JsonReader reader, Type objectType, string existingValue, bool hasExistingValue, JsonSerializer serializer)
{
var tokens = new Dictionary<string, object>();
serializer.Populate(reader, tokens);
return (string) tokens["fmt"] ?? throw new JsonReaderException(#"""fmt"" not found in sharesShortPriorMonth");
}
}
The Populate method comes in very handy in this case because we can fill in a dictionary containing all the json attributes, eventually we just need to retrieve the fmt property.
The Stocks class becomes:
public class Stocks
{
[JsonConverter(typeof(CustomConverter))]
public string sharesShortPriorMonth { get; set; }
}

You need either change your sharesShortPriorMonth to be of type representing the existing json structure (you can include only needed properties):
public SharesShortPriorMonth sharesShortPriorMonth { get; set; }
public class SharesShortPriorMonth
{
public string fmt { get; set; }
}
Or create custom converter and mark field with corresponding attribute.

I would use something like this
public class Stocks
{
... another properties
[JsonProperty("sharesShortPriorMonth")]
public JObject sharesShortPriorMonthJo { set { sharesShortPriorMonth = (string) value["fmt"]; }}
[JsonIgnore]
public string sharesShortPriorMonth { get; private set;}
}

Related

Using JsonConvert.DeserializeObject to dynamically choose class

So I am making an api call and I need to use JsonConvert.DeserializeObject to convert it to a class. The Json structure comes back as the following
{
"fcResponse": {
"responseData": {
"fcRequest": {
"mail": "Emails",
"outlookMail": "Outlook Emails",
(etc.)
}
}
}
}
The problem is that the values that come back inside "fcRequest" varies based on the parameters I am sending.
The class structure is as follows so far
public class GetSubModulesResponse : BaseResponse
{
[JsonProperty("fcResponse")]
public SubModuleResponse Response { get; set; }
}
public class SubModuleResponse
{
[JsonProperty("responseData")]
public SubModuleData Data { get; set; }
}
public class SubModuleData
{
[JsonProperty("fcRequest")]
public SubModuleFIMRequest RequestFIM { get; set; }
[JsonProperty("fcRequest")]
public SubModuleFSRequest RequestFS { get; set; }
}
And this is the basic call structure
GetSubModulesResponse subModuleResponse = new GetSubModulesResponse();
var response = SubmitAPICall();
subModuleResponse = JsonConvert.DeserializeObject<GetSubModulesResponse>(response);
Now I know I obviously can't have the same JsonProperty on both RequestFIM and RequestFS, but what I'm trying to do is somehow find a way to switch which one of those two properties I should use based on a variable.
One option is to go with a custom (de-)serializer for the element. This way, you can still least benefit from automatic deserialization in most spots and get the flexibility where you need it. I'm assuming you're using Newtonsoft JSON / JSON.NET.
Let's introduce a base class for the fcRequest elements first.
public enum ResponseType
{
FIM, FS
}
public abstract class ResponseBase
{
[JsonIgnore]
public abstract ResponseType ResponseType { get; }
}
By adding a ResponseType here you can simplify consuming code; if you can use type based pattern matching, you may not even need it.
I obviously have no idea what your domain entities are, but for the sake of the argument, the SubModuleFIMRequest is now going to contain the mail addresses. In addition, it also derives from said ResponseBase:
public class SubModuleFIMRequest : ResponseBase
{
public override ResponseType ResponseType => ResponseType.FIM;
[JsonProperty("mail")]
public string Mail { get; set; }
[JsonProperty("outlookMail")]
public string OutlookMail { get; set; }
}
Next, you'd implement a JsonConverter<ResponseBase>; to make life easy, it can deserialize the responseData content into a JObject first. In doing so, you'll be able to introspect the properties of the element, which in turn (hopefully) allows you to come up with a heuristic to determine the element's actual type.
Once you know the type, you convert the JObject to a concrete instance. Here's an example:
public class ResponseDataConverter : JsonConverter<ResponseBase>
{
/// <inheritdoc />
public override bool CanWrite => false;
/// <inheritdoc />
public override ResponseBase ReadJson(JsonReader reader, Type objectType, ResponseBase existingValue, bool hasExistingValue, JsonSerializer serializer)
{
var jObject = serializer.Deserialize<JObject>(reader);
// Now, decide tye type by matching patterns.
if (jObject.TryGetValue("mail", out var mailToken))
{
return jObject.ToObject<SubModuleFIMRequest>();
}
// TODO: Add more types as needed
// If nothing matches, you may choose to throw an exception,
// return a catchall type (e.g. wrapping the JObject), or just
// return a default value as a last resort.
throw new JsonSerializationException();
}
/// <inheritdoc />
public override void WriteJson(JsonWriter writer, ResponseBase value, JsonSerializer serializer) => throw new NotImplementedException();
}
Note that the serializer doesn't need to write, so we're just throwing in WriteJson.
What's left is to annotate the SubModuleData's fcProperty property with a JsonConverter attribute pointing to the converter type:
public class SubModuleData
{
[JsonProperty("fcRequest")]
[JsonConverter(typeof(ResponseDataConverter))]
public ResponseBase FcRequest { get; set; }
}
I hope that gets you started. As was mentioned in other comments and answers: If you can influence the API returning the JSON in the first place, try changing that instead.
If you're in control of the Json being returned, I'd highly recommend returning each object under it's own property and set whatever's not needed to null.
If that's not an option, another thing that could work is deserializing "fcRequest" to dynamic and then try casting to the types it may be or casting based on another property name. That's not very clean.
Another interesting approach in a different Json lib (Jil) is Unions.
"Jil has limited support for "unions" (fields on JSON objects that may contain one of several types), provided that they can be distiguished by their first character."

Is it possible to deserialize into property rather than an object

Appologies if its already been asked, I could not find anything helpful to my situation.
I need to deserialize a JSON in a property of my object instead of a whole object. The reason I am trying to do it, is that is simply generics.
I have the following situation
For instance I have
Class User
{
int UserId {get;set;}
string Name {get;set;
}
Class Wanted : CustomClass
{
User[] Users {get;set;}
public override void Map(){ }
public override void Scan(){ }
}
My Json is:
[
{
"userId": 1,
"name": "Josh"
},
{
"userId": 5,
"name" : "Martin"
}
]
Is it possible to deserialize(+ generics) my JSON directly into my Wanted class instead of serializing into A and then assign it into Wanted ?
The goal is after the serialization I will have object with type Wanted and an array with 2 users in it.
Since the JSON does not match the class you want to deserialize into, and you cannot change the JSON, you will need to use a custom JsonConverter to bridge the gap.
To make it work you'll need to introduce an interface IHasUsers which your Wanted class (or its base class) will need to implement:
interface IHasUsers
{
User[] Users { get; set; }
}
class Wanted : CustomClass, IHasUsers
{
public User[] Users { get; set; }
...
}
Then you can make a generic converter which will instantiate the Wanted class (or any other class which implements IHasUsers) and populate the Users property:
class UserListConverter<T> : JsonConverter where T: IHasUsers, new()
{
public override bool CanConvert(Type objectType)
{
return typeof(IHasUsers).IsAssignableFrom(objectType);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JArray array = JArray.Load(reader);
T obj = new T() { Users = array.ToObject<User[]>() };
return obj;
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Then you can deserialize your JSON like this:
Wanted wanted = JsonConvert.DeserializeObject<Wanted>(json, new UserListConverter<Wanted>());
Here is a demo: https://dotnetfiddle.net/KL6Ok6
Hope this is what you were looking for.
Since Wanted is "your desired class", there needs to be an instance of Wanted created somewhere. You might just as well create it yourself rather than having a derserializer do it for you. Once you have done this you can simply set the Users property to the deserialized data:
var wanted = new Wanted() { Users = JsonConvert.DeSerialize<User[]>(myString) };
You don't deserialize some data "into a property" without deserializing it to some object of some type first. Once you have done this you can then set the property to the object that contains the deserialized data.
There is nothing generic about Wanted here though and the deserializer cannot be supposed to figure out that it should create a Wanted or any other type unless you specify the type to derserialize the data to somewhere.
And there is no point of deserializing the data to a type defined at compile time if you don't know that the data matches this type. Then you might as well create an anonymous object or a dictionary of key/value pairs.
You can use Newtonsoft.json . Try below
var files = JArray.Parse(YourJSON);
var recList = files.SelectTokens("$").ToList();
foreach (JObject item in recList.Children())
{
foreach (JProperty prop in item.Children())
{
string key = prop.Name.ToString();
string value = prop.Value.ToString();
// and add these to an array
}
}

How to force the serialization/deserialization of my custom type?

Here is my code:
void Main()
{
var test = new Order()
{
Id = Guid.NewGuid(),
Title = "Test",
Code = new Code("O-123456789") // TODO create a Code.NewCode() later
};
var line = Newtonsoft.Json.JsonConvert.SerializeObject(test).ToString();
Console.WriteLine(line);
}
// Define other methods and classes here
public class Order
{
public Guid Id { get; set; }
public Code Code { get; set; }
public string Title { get; set; }
}
public class Code
{
public string code;
public Code(string code)
{
this.code = code;
}
}
On the console I get this result:
{"Id":"227599fe-c834-4330-84e5-2018abe59e35","Code":{"code":"O-123456789"},"Title":"Test"}
But I want this:
{"Id":"227599fe-c834-4330-84e5-2018abe59e35","Code":"O-123456789","Title":"Test"}
So how can I force my Code type to serialize like I want. Actually, I want the same behavior of Guid(). Or find a way to implement String(). Could you help me on this.
I know I can probably use some attribute to force JSON serialization but I would like something that work for all serialization exactly like the Guid()
You can create a new JsonConverter that deals with your type and then serializes it how you like:
public class CodeSerializer : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var code = value as Code;
writer.WriteValue(code.code);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override bool CanConvert(Type objectType)
{
return typeof(Code).IsAssignableFrom(objectType);
}
}
Once we have this you can plug it in to your SerializeObject method by setting some properties on JsonSerializerSettings:
var jsonSerializerSettings = new JsonSerializerSettings();
jsonSerializerSettings.Converters.Insert(0, new CodeSerializer());
var line = Newtonsoft.Json.JsonConvert.SerializeObject(test, jsonSerializerSettings).ToString();
Console.WriteLine(line);
// {"Id":"2010e737-a9e8-4b77-bde6-1c50e92c6a30","Code":"O-123456789","Title":"Test"}
Maybe you can do it like this;
public class Order
{
public Guid Id { get; set; }
[JsonIgnore]
public Code Code { get; set; }
public string SerializedCode
{
get
{
if (Code != null)
{
return Code.code;
}
return string.Empty;
}
}
public string Title { get; set; }
}
Output : {"Id":"227599fe-c834-4330-84e5-2018abe59e35","SerializedCode":"O-123456789","Title":"Test"}
Actually, you can't do it for all serialization actions. There is no generic way to perform it. Maybe you can provide own serializer class. But I think, it wouldn't be a good solution. You don't want serialized output and class object
to be different from each other. It can be cause another problems. I suggest you to change your class and properties to perform it.
The answer is the one given in comment by David Watts:
Json.Net converts the .Net Primitive of a Guid to a string (JSON Primitive) under the hood. All details on newtonsoft.com/json/help/html/SerializationGuide.htm
At a high level, the Json.NET serializer will convert primitive .NET values into primitive JSON values, will convert .NET arrays and collections to JSON arrays, and will convert everything else to JSON objects.
For other custom serialization I must use a JsonConverter as explained by Kevin Smith.

Serialize Dynamic Property Name for an Object using JSON.NET

I'm using JSON.NET for serialization of my objects for connecting to a REST API. One of the properties in my object that needs to be serialized to JSON has a dynamic property name.
If the value contained in the struct for this property is a numeric value, then the JSON property is "type_id", however if this value is a string value, then the JSON property name is "type_code". I attempted to use a custom JsonConverter for this, but I get a JsonWriterException with this message when I attempt to serialize:
"Token PropertyName in state Property would result in an invalid JSON object. Path ''."
Below is a subset of my object, as seen below I didn't specify a property name in my object for that as such:
[JsonProperty("title",Required=Required.Always,Order=1)]
public string Title { get; set; }
[JsonProperty("date",Order=3)]
[JsonConverter(typeof(IsoDateTimeConverter))]
public DateTime Date { get; set; }
[JsonProperty(Order=2)]
[JsonConverter(typeof(TypeIdentifierJsonConverter))]
public TypeIdentifier DocTypeIdentifier { get; set; }
In the TypeIdentifier class I have the following in my WriteJson() method:
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
TypeIdentifier docTypeId;
id= (TypeIdentifier) value;
writer.WritePropertyName(id.ParameterName);
writer.WriteValue(id.Value);
}
However, I am assuming it's defaulting to the name of the object's property instead of my custom one, causing two property names for a single value within the JSON string. How can the property name be set dynamically for this, since the JsonPropertyAttribute tag appears to pull the object's property name when not specified explicitly?
NOTE: This object will never need to be deserialized from this app.
EDIT: This object is tagged with the [JsonObject(MemberSerialization.OptIn)] attribute
A JsonConverter cannot set the name of a property in a parent object. When the converter's WriteJson method is called, the property name has already been written to the JSON; the writer is expecting only a value that point. That is why you are getting an error. In order to make this work, the custom converter would have to be made for the parent object. That converter would then be responsible for writing the property names and values of its children.
Follow-up
It is possible to write a converter for the parent object such that the JSON attributes applied to it are still respected, while still achieving the result you want. I'll outline the approach below.
First, a little bit of setup. Since you did not say what your class was called, I'll assume for this example that it is called Document. We only need to make one substantive change to it, and that is to remove the [JsonConverter] attribute from the DocTypeIdentifier property. So we have:
[JsonObject(MemberSerialization.OptIn)]
class Document
{
[JsonProperty("title", Required = Required.Always, Order = 1)]
public string Title { get; set; }
[JsonProperty("date", Order = 3)]
[JsonConverter(typeof(IsoDateTimeConverter))]
public DateTime Date { get; set; }
[JsonProperty(Order = 2)]
public TypeIdentifier DocTypeIdentifier { get; set; }
public string OtherStuff { get; set; }
}
You also did not show the code for the TypeIdentifier class, so I'll just assume it looks like this, for sake of example:
class TypeIdentifier
{
public string Value { get; set; }
public string ParameterName { get; set; }
}
With that out of the way, we can make the converter. The approach is fairly straightforward: we load the Document into a JObject, taking advantage of the fact that it respects the attributes applied, then go back and fix the serialization of the DocTypeIdentifier since it needs special handling. Once we have that, we write out the JObject to the JsonWriter. Here is the code:
class DocumentConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(Document));
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
Document doc = (Document)value;
// Create a JObject from the document, respecting existing JSON attribs
JObject jo = JObject.FromObject(value);
// At this point the DocTypeIdentifier is not serialized correctly.
// Fix it by replacing the property with the correct name and value.
JProperty prop = jo.Children<JProperty>()
.Where(p => p.Name == "DocTypeIdentifier")
.First();
prop.AddAfterSelf(new JProperty(doc.DocTypeIdentifier.ParameterName,
doc.DocTypeIdentifier.Value));
prop.Remove();
// Write out the JSON
jo.WriteTo(writer);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Now we have the converter, but the catch is we cannot simply decorate the Document class with a [JsonConverter] attribute in order to use it. If we did, we would end up with a recursive loop as the converter tried to use itself when we loaded the document into the JObject. So instead, we need to create an instance of the converter and pass it to the serializer via settings. The converter's CanConvert method ensures it gets used on the correct class. The JObject.FromObject method uses a different serializer instance internally, so it does not see the DocumentConverter and thus does not get into trouble.
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.Converters.Add(new DocumentConverter());
string json = JsonConvert.SerializeObject(doc, settings);
Here is a demo showing the converter in action:
class Program
{
static void Main(string[] args)
{
Document doc = new Document
{
Title = "How to write a JSON converter",
Date = DateTime.Today,
DocTypeIdentifier = new TypeIdentifier
{
ParameterName = "type_id",
Value = "26"
},
OtherStuff = "this should not appear in the JSON"
};
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.Converters.Add(new DocumentConverter());
settings.Formatting = Formatting.Indented;
string json = JsonConvert.SerializeObject(doc, settings);
Console.WriteLine(json);
}
}
Here is the output from the above:
{
"title": "How to write a JSON converter",
"type_id": "26",
"date": "2014-03-28T00:00:00-05:00"
}

Custom conversion of specific objects in JSON.NET

I'm using JSON.NET to serialize some of my objects, and i'd like to know if there is a simple way to override the default json.net converter only for a specific object?
Currently I have the following class:
public class ChannelContext : IDataContext
{
public int Id { get; set; }
public string Name { get; set; }
public IEnumerable<INewsItem> Items { get; set; }
}
JSON.NET currently serializes the above like:
{
"Id": 2,
"Name": "name value",
"Items": [ item_data_here ]
}
Is it possible just for that specific class to format it this way instead:
"Id_2":
{
"Name": "name value",
"Items": [ item data here ]
}
I'm kinda new to JSON.NET.. I was wondering if the above has something to do with writing a custom converter. I wasn't able to find any concrete examples on how to write one, If anyone can point me out to a specific source, I'll really appreciate it.
I need to find a solution which makes that specific class always convert the same, because the above context is a part of an even bigger context which the JSON.NET default converter converts just fine.
Hope my question is clear enough...
UPDATE:
I've found how to create a new custom converter (by creating a new class which inherits from JsonConverter and override it's abstract methods), I overriden the WriteJson method as follows:
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
ChannelContext contextObj = value as ChannelContext;
writer.WriteStartObject();
writer.WritePropertyName("id_" + contextObj.Id);
writer.WriteStartObject();
writer.WritePropertyName("Name");
serializer.Serialize(writer, contextObj.Name);
writer.WritePropertyName("Items");
serializer.Serialize(writer, contextObj.Items);
writer.WriteEndObject();
writer.WriteEndObject();
}
This indeed does the job successfully, but...
I'm intrigued if there's a way to serialize the rest of the object properties by reusing the default JsonSerializer (or converter for that matter) instead of manually "Writing" the object using the jsonwriter methods.
UPDATE 2:
I'm trying to get a more generic solution and came up with the following:
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteStartObject();
// Write associative array field name
writer.WritePropertyName(m_FieldNameResolver.ResolveFieldName(value));
// Remove this converter from serializer converters collection
serializer.Converters.Remove(this);
// Serialize the object data using the rest of the converters
serializer.Serialize(writer, value);
writer.WriteEndObject();
}
This works fine when adding the converter manually to the serializer, like this:
jsonSerializer.Converters.Add(new AssociativeArraysConverter<DefaultFieldNameResolver>());
jsonSerializer.Serialize(writer, channelContextObj);
But doesn't work when using the [JsonConverter()] attribute set to my custom coverter above the ChannelContext class because of a self reference loop that occurs when executing:
serializer.Serialize(writer, value)
This is obviously because my custom converter is now considered the default converter for the class once it is set with the JsonConverterAttribute, so I get an inifinite loop.
The only thing I can think of, in order to solve this problem is inheriting from a basic, jsonconverter class, and calling the base.serialize() method instead...
But is such a JsonConverter class even exists?
Thanks a lot!
Mikey
If anyone's interested in my solution:
When serializing certain collections, I wanted to create an associative json array instead of a standard json array, so my colleague client side developer can reach those fields efficiently, using their name (or key for that matter) instead of iterating through them.
consider the following:
public class ResponseContext
{
private List<ChannelContext> m_Channels;
public ResponseContext()
{
m_Channels = new List<ChannelContext>();
}
public HeaderContext Header { get; set; }
[JsonConverter(
typeof(AssociativeArraysConverter<ChannelContextFieldNameResolver>))]
public List<ChannelContext> Channels
{
get { return m_Channels; }
}
}
[JsonObject(MemberSerialization = MemberSerialization.OptOut)]
public class ChannelContext : IDataContext
{
[JsonIgnore]
public int Id { get; set; }
[JsonIgnore]
public string NominalId { get; set; }
public string Name { get; set; }
public IEnumerable<Item> Items { get; set; }
}
Response context contains the whole response which is written back to the client, like you can see, it includes a section called "channels", And instead of outputting the channelcontexts in a normal array, I'd like to be able to output in the following way:
"Channels"
{
"channelNominalId1":
{
"Name": "name value1"
"Items": [ item data here ]
},
"channelNominalId2":
{
"Name": "name value2"
"Items": [ item data here ]
}
}
Since I wanted to use the above for other contexts as well, and I might decide to use a different property as their "key", or might even choose to create my own unique name, which doesn't have to do with any property, I needed some sort of a generic solution, therefore I wrote a generic class called AssociativeArraysConverter, which inherits from JsonConverter in the following manner:
public class AssociativeArraysConverter<T> : JsonConverter
where T : IAssociateFieldNameResolver, new()
{
private T m_FieldNameResolver;
public AssociativeArraysConverter()
{
m_FieldNameResolver = new T();
}
public override bool CanConvert(Type objectType)
{
return typeof(IEnumerable).IsAssignableFrom(objectType) &&
!typeof(string).IsAssignableFrom(objectType);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
IEnumerable collectionObj = value as IEnumerable;
writer.WriteStartObject();
foreach (object currObj in collectionObj)
{
writer.WritePropertyName(m_FieldNameResolver.ResolveFieldName(currObj));
serializer.Serialize(writer, currObj);
}
writer.WriteEndObject();
}
}
And declared the following Interface:
public interface IAssociateFieldNameResolver
{
string ResolveFieldName(object i_Object);
}
Now all is left to do, is create a class which implements IAssociateFieldNameResolver's single function, which accepts each item in the collection, and returns a string based on that object, which will act as the item's associative object's key.
Example for such a class is:
public class ChannelContextFieldNameResolver : IAssociateFieldNameResolver
{
public string ResolveFieldName(object i_Object)
{
return (i_Object as ChannelContext).NominalId;
}
}

Categories

Resources