RestSharp ignores JSON attributes when serializing instance [duplicate] - c#

I have an object
var testTcc = new TrendingConfigurationConfigDto
{
TrendingConfigurationId =1,
ConfigId = 1,
DeviceId = 1,
Selected = true,
YAxisPosition = YAxisPosition.Left,
Order = 1,
Color = "#ffffff",
Configuration = new BO.Shared.Dtos.List.ConfigurationListDto
{
Id = 1,
Name = "configuration",
Alias = "configuationAlias",
EnableEdit = true,
IsBusinessItem = true
},
Device = new BO.Shared.Dtos.List.DeviceListDto
{
Id = 1,
Name = "Device"
}
};
when I serialized it into json as
var jsonTcc = SimpleJson.SerializeObject(testTcc);
it returned string containing json object with YAxisPosition = 1, and when I tried deserializing it using
testTcc = SimpleJson.DeserializeObject<TrendingConfigurationConfigDto>(jsonTcc);
It is giving an exception System.InvalidCastException with message 'Specified cast is not valid'.
I tried changing YAxisPosition value in json string to string "1" or "Left" it was always giving me the same error, until I removed the property YAxisPosition from json string.
I might be missing some thing (an Attribute on enum property or something similar).
Please help me finding a way so that I can Serialize and De-serialize an object which contains Enum type property, using RestSharp.
Note: I tried successful serialization and de-serialization using NewtonSoft. but I do not want a dependency of my Web API Client on NetwonSoft, as I am already using RestSharp.

RestSharp removed JSON.NET support in v103.0. The default Json Serializer is no longer compatible with Json.NET. You have a couple of options if you want to continue using JSON.NET and maintain backwards compatibility. Beyond that, JSON.NET has more capability and may solve your issues over using the basic .NET serializer which RestSharp now depends on.
Plus you can use the [EnumMember] properties to define custom mappings during deserialization.
Option 1: Implement a custom serializer that uses JSON.NET
To use Json.NET for serialization, copy this code:
/// <summary>
/// Default JSON serializer for request bodies
/// Doesn't currently use the SerializeAs attribute, defers to Newtonsoft's attributes
/// </summary>
public class JsonNetSerializer : ISerializer
{
private readonly Newtonsoft.Json.JsonSerializer _serializer;
/// <summary>
/// Default serializer
/// </summary>
public JsonSerializer() {
ContentType = "application/json";
_serializer = new Newtonsoft.Json.JsonSerializer {
MissingMemberHandling = MissingMemberHandling.Ignore,
NullValueHandling = NullValueHandling.Include,
DefaultValueHandling = DefaultValueHandling.Include
};
}
/// <summary>
/// Default serializer with overload for allowing custom Json.NET settings
/// </summary>
public JsonSerializer(Newtonsoft.Json.JsonSerializer serializer){
ContentType = "application/json";
_serializer = serializer;
}
/// <summary>
/// Serialize the object as JSON
/// </summary>
/// <param name="obj">Object to serialize</param>
/// <returns>JSON as String</returns>
public string Serialize(object obj) {
using (var stringWriter = new StringWriter()) {
using (var jsonTextWriter = new JsonTextWriter(stringWriter)) {
jsonTextWriter.Formatting = Formatting.Indented;
jsonTextWriter.QuoteChar = '"';
_serializer.Serialize(jsonTextWriter, obj);
var result = stringWriter.ToString();
return result;
}
}
}
/// <summary>
/// Unused for JSON Serialization
/// </summary>
public string DateFormat { get; set; }
/// <summary>
/// Unused for JSON Serialization
/// </summary>
public string RootElement { get; set; }
/// <summary>
/// Unused for JSON Serialization
/// </summary>
public string Namespace { get; set; }
/// <summary>
/// Content type for serialized content
/// </summary>
public string ContentType { get; set; }
}
and register it with your client:
var client = new RestClient();
client.JsonSerializer = new JsonNetSerializer();
Option 2: Use a nuget package to use JSON.NET
Instead of doing all of that and having a custom JSON serializer spread throughout your projects is to just use this nuget package: https://www.nuget.org/packages/RestSharp.Newtonsoft.Json. It allows you to use an inherited RestRequest object that defaults to using Newtonsoft.JSON internally like this:
var request = new RestSharp.Newtonsoft.Json.RestRequest(); // Uses JSON.NET
The other option is to set it on every request like this:
var request = new RestRequest();
request.JsonSerializer = new NewtonsoftJsonSerializer();
Disclaimer: I created this project after getting frustrated with having a custom serializer laying around in my projects. I created this to keep things clean and hopefully help others who want backwards compatibility with their RestSharp code that worked prior to v103.

Found solution for this problem:
private IRestClient GetRestClient()
{
return new RestClient(url)
.AddDefaultHeader("Authorization", $"Bearer {token.AccessToken}")
.AddDefaultHeader("Accept", "*/*")
.AddDefaultHeader("Accept-Encoding", "gzip, deflate, br")
.AddDefaultHeader("Connection", "close")
.UseSystemTextJson(new JsonSerializerOptions
{
Converters = { new JsonStringEnumConverter() }
});
}
I.e. instructed RestSharp to use System.Text.Json serializer and then instructed the serializer to use JsonStringEnumConverter class to serialize/deserialize enums.

I got it working with the help of PocoJsonSerializerStrategy. RestSharp allows you to specify your own serialization/deserialization strategy, so I created my own strategy that handles enums for me:
public class HandleEnumsJsonSerializerStrategy : PocoJsonSerializerStrategy
{
public override object DeserializeObject(object value, Type type)
{
if (type.IsEnum)
return Enum.Parse(type, (string)value);
else
return base.DeserializeObject(value, type);
}
}
Now you can pass an object of this class to SimpleJson.DeserializeObject call, like this and your enums are handled elegantly:
SimpleJson.DeserializeObject<JsonObject>(Response.Content, Strategy);

If you are using Enumeration class pattern you can user a JsonConverter approach. It means you need to create a custom converter and use JsonConverter attribute on the property you need to serialize. Example is following:
using Newtonsoft.Json;
using System;
namespace MyAwesomeAPI
{
public class EnumerationConverter<TEnum> : JsonConverter
where TEnum : Enumeration
{
public override bool CanConvert(Type objectType) => objectType == typeof(TEnum);
public override bool CanRead { get => true; }
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var uknown = Enumeration.FromDisplayName<TEnum>("Unknown");
if (reader.TokenType == JsonToken.Null)
{
return null;
}
if (reader.TokenType == JsonToken.String)
{
var enumText = reader.Value?.ToString();
if (string.IsNullOrEmpty(enumText))
{
return uknown;
}
try
{
return Enumeration.FromDisplayName<TEnum>(enumText);
}
catch (Exception)
{
return uknown;
}
}
return uknown;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteValue(value.ToString());
}
}
}
Where our enum looks something like:
public class CardType : Enumeration
{
public static CardType Unknown = new(0, nameof(Unknown));
public static CardType Amex = new(1, nameof(Amex));
public static CardType Visa = new(2, nameof(Visa));
public static CardType MasterCard = new(3, nameof(MasterCard));
public CardType(int id, string name) : base(id, name)
{
}
}
And last thing is apply the attribute to a property
public class CreditCardResponse
{
[JsonProperty("card_number")]
public string CardNumber { get; set; }
[JsonProperty("card_type")]
[JsonConverter(typeof(EnumerationConverter<CardType>))]
public CardType CardType { get; set; }
}

Related

'RestClient' does not contain a definition for 'JsonSerializer' and no extension method 'JsonSerializer'

As i read readme.txt for latest RestSharp:
*** IMPORTANT CHANGE IN RESTSHARP VERSION 103 ***
In 103.0, JSON.NET was removed as a dependency.
If this is still installed in your project and no other libraries depend on
it you may remove it from your installed packages.
There is one breaking change: the default Json*Serializer* is no longer
compatible with Json.NET. To use Json.NET for serialization, copy the code
from https://github.com/restsharp/RestSharp/blob/86b31f9adf049d7fb821de8279154f41a17b36f7/RestSharp/Serializers/JsonSerializer.cs
and register it with your client:
var client = new RestClient();
client.JsonSerializer = new YourCustomSerializer();
The default Json*Deserializer* is mostly compatible, but it does not support
all features which Json.NET has (like the ability to support a custom [JsonConverter]
by decorating a certain property with an attribute). If you need these features, you
must take care of the deserialization yourself to get it working.
Im already installed Newtonsoft.Json with nu package manager, and im trying to register Json.NET to client variable, but didn't work. here is my code:
private void Form1_Load(object sender, EventArgs e)
{
var client = new RestClient("http://homestead.app/vendor");
client.JsonSerializer = new JsonSerializer(); <-- HERE IS THE ERROR
var request = new RestRequest("", Method.GET);
IRestResponse response = client.Execute(request);
var content = response.Content; // raw content as string
textBox1.Text = content;
}
the client.JsonSerializer property is not available.
Please kindly help me. thanks
The serializers for RestSharp must implement two interfaces:
RestSharp.Serializers.ISerializer
RestSharp.Serializers.IDeserializer
You must wrap the serializer from Newtonsoft to type with these interfaces.
There is working code from one project I worked on:
/// <summary>
/// Default JSON serializer for request bodies
/// Doesn't currently use the SerializeAs attribute, defers to Newtonsoft's attributes
/// </summary>
/// <remarks>
/// Based on http://blog.patrickmriley.net/2014/02/restsharp-using-jsonnet-serializer.html
/// </remarks>
public class RestSharpJsonNetSerializer : RestSharp.Serializers.ISerializer, RestSharp.Deserializers.IDeserializer
{
private readonly JsonSerializer serializer;
/// <summary>
/// Default serializer
/// </summary>
public RestSharpJsonNetSerializer()
{
this.ContentType = "application/json";
this.serializer = new JsonSerializer
{
MissingMemberHandling = MissingMemberHandling.Ignore,
NullValueHandling = NullValueHandling.Include,
DefaultValueHandling = DefaultValueHandling.Include
};
}
/// <summary>
/// Default serializer with overload for allowing custom Json.NET settings
/// </summary>
public RestSharpJsonNetSerializer(JsonSerializer serializer)
{
this.ContentType = "application/json";
this.serializer = serializer;
}
/// <summary>
/// Unused for JSON Serialization
/// </summary>
public string DateFormat { get; set; }
/// <summary>
/// Unused for JSON Serialization
/// </summary>
public string RootElement { get; set; }
/// <summary>
/// Unused for JSON Serialization
/// </summary>
public string Namespace { get; set; }
/// <summary>
/// Content type for serialized content
/// </summary>
public string ContentType { get; set; }
/// <summary>
/// Serialize the object as JSON
/// </summary>
/// <param name="obj">Object to serialize></param>
/// <returns>JSON as String</returns>
public string Serialize(object obj)
{
using (var stringWriter = new StringWriter())
{
using (var jsonTextWriter = new JsonTextWriter(stringWriter))
{
jsonTextWriter.Formatting = Formatting.Indented;
jsonTextWriter.QuoteChar = '"';
this.serializer.Serialize(jsonTextWriter, obj);
var result = stringWriter.ToString();
return result;
}
}
}
public T Deserialize<T>(RestSharp.IRestResponse response)
{
using (var strReader = new StringReader(response.Content))
{
using (var jsonReader = new JsonTextReader(strReader))
{
var data = this.serializer.Deserialize<T>(jsonReader);
return data;
}
}
}
}
The new readme.txt is updated at least in GitHub but the package still contains the old code.
So the answer of #TcKs is correct:
var request = new RestRequest();
request.JsonSerializer = new Shared.JsonSerializer();
var client = new RestClient();
client.Post(request);
It will be easy if you using mapper object when return
public YourResultBase Login(string username, string password)
{
var client = new RestClient("apiUrl");
var request = new RestRequest(Method.GET)
{
OnBeforeDeserialization = resp => { resp.ContentType = "application/json"; }
};
request.AddHeader("Cache-Control", "no-cache");
IRestResponse<YourResultBase> response = client.Execute<YourResultBase>(request);
var result = response.Data;
return result;
}

REST api with C#.NET complex json data in body posting

I want to use sms gateway in my app. that's why I've contact with an operator and the operator give me a api format.
URL: https://ideabiz.lk/apicall/smsmessaging/v2/outbound/3313/requests
Request header
Content-Type: application/json
Authorization: Bearer [access token]
Accept: application/json
Body
{
"outboundSMSMessageRequest": {
"address": [
"tel:+94771234567"
],
"senderAddress": "tel:12345678",
"outboundSMSTextMessage": {
"message": "Test Message"
},
"clientCorrelator": "123456",
"receiptRequest": {
"notifyURL": "http://128.199.174.220:1080/sms/report",
"callbackData": "some-data-useful-to-the-requester"
},
"senderName": "ACME Inc."
}
}
Now, I've code it :
RestClient client = new RestClient(#"https://ideabiz.lk/");
RestRequest req = new RestRequest(#"apicall/smsmessaging/v2/outbound/3313/requests", Method.POST);
req.AddHeader("Content-Type", #"application/json");
req.AddHeader("Authorization", #"Bearer " + accessToken.ToString());
req.AddHeader("Accept", #"application/json");
string jSon_Data = #"{'outboundSMSMessageRequest': {'address': ['tel:+94768769027'],'senderAddress': 'tel:3313','outboundSMSTextMessage': {'message': 'Test Message : " + System.DateTime.Now.ToString() + "'},'clientCorrelator': '123456','receiptRequest': {'notifyURL': 'http://128.199.174.220:1080/sms/report','callbackData': 'some-data-useful-to-the-requester'},'senderName': ''}}";
JObject json = JObject.Parse(jSon_Data);
req.AddBody(json);
IRestResponse response = client.Execute(req);
string x = response.Content.ToString();
Console.WriteLine(x.ToString());
When i execute this program, in the line
req.AddBody(json);
my system crash and give error message that:
An unhandled exception of type 'System.StackOverflowException' occurred in System.Windows.Forms.dll
How can i post complex JSON by using C#.NET ?
You have two problems here:
You need to set RequestFormat = DataFormat.Json before the call to AddBody:
req.RequestFormat = DataFormat.Json;
req.AddBody(json);
Without setting the parameter, RestSharp tries to serialize the JObject to XML and falls into an infinite recursion somewhere -- most likely trying to serialize JToken.Parent.
The more recent versions of RestSharp no longer use Json.NET as their JSON serializer:
There is one breaking change: the default Json*Serializer* is no longer
compatible with Json.NET. To use Json.NET for serialization, copy the code
from https://github.com/restsharp/RestSharp/blob/86b31f9adf049d7fb821de8279154f41a17b36f7/RestSharp/Serializers/JsonSerializer.cs
and register it with your client:
var client = new RestClient();
client.JsonSerializer = new YourCustomSerializer();
RestSharp's new built-in JSON serializer doesn't understand JObject so you need to follow the instructions above if you are using one of these more recent versions, Create:
public class JsonDotNetSerializer : ISerializer
{
private readonly Newtonsoft.Json.JsonSerializer _serializer;
/// <summary>
/// Default serializer
/// </summary>
public JsonDotNetSerializer() {
ContentType = "application/json";
_serializer = new Newtonsoft.Json.JsonSerializer {
MissingMemberHandling = MissingMemberHandling.Ignore,
NullValueHandling = NullValueHandling.Include,
DefaultValueHandling = DefaultValueHandling.Include
};
}
/// <summary>
/// Default serializer with overload for allowing custom Json.NET settings
/// </summary>
public JsonDotNetSerializer(Newtonsoft.Json.JsonSerializer serializer){
ContentType = "application/json";
_serializer = serializer;
}
/// <summary>
/// Serialize the object as JSON
/// </summary>
/// <param name="obj">Object to serialize</param>
/// <returns>JSON as String</returns>
public string Serialize(object obj) {
using (var stringWriter = new StringWriter()) {
using (var jsonTextWriter = new JsonTextWriter(stringWriter)) {
jsonTextWriter.Formatting = Formatting.Indented;
jsonTextWriter.QuoteChar = '"';
_serializer.Serialize(jsonTextWriter, obj);
var result = stringWriter.ToString();
return result;
}
}
}
/// <summary>
/// Unused for JSON Serialization
/// </summary>
public string DateFormat { get; set; }
/// <summary>
/// Unused for JSON Serialization
/// </summary>
public string RootElement { get; set; }
/// <summary>
/// Unused for JSON Serialization
/// </summary>
public string Namespace { get; set; }
/// <summary>
/// Content type for serialized content
/// </summary>
public string ContentType { get; set; }
}
And then do:
RestRequest req = new RestRequest(#"apicall/smsmessaging/v2/outbound/3313/requests", Method.POST);
req.JsonSerializer = new JsonDotNetSerializer();

RestSharp deserialization of Enum Type Property

I have an object
var testTcc = new TrendingConfigurationConfigDto
{
TrendingConfigurationId =1,
ConfigId = 1,
DeviceId = 1,
Selected = true,
YAxisPosition = YAxisPosition.Left,
Order = 1,
Color = "#ffffff",
Configuration = new BO.Shared.Dtos.List.ConfigurationListDto
{
Id = 1,
Name = "configuration",
Alias = "configuationAlias",
EnableEdit = true,
IsBusinessItem = true
},
Device = new BO.Shared.Dtos.List.DeviceListDto
{
Id = 1,
Name = "Device"
}
};
when I serialized it into json as
var jsonTcc = SimpleJson.SerializeObject(testTcc);
it returned string containing json object with YAxisPosition = 1, and when I tried deserializing it using
testTcc = SimpleJson.DeserializeObject<TrendingConfigurationConfigDto>(jsonTcc);
It is giving an exception System.InvalidCastException with message 'Specified cast is not valid'.
I tried changing YAxisPosition value in json string to string "1" or "Left" it was always giving me the same error, until I removed the property YAxisPosition from json string.
I might be missing some thing (an Attribute on enum property or something similar).
Please help me finding a way so that I can Serialize and De-serialize an object which contains Enum type property, using RestSharp.
Note: I tried successful serialization and de-serialization using NewtonSoft. but I do not want a dependency of my Web API Client on NetwonSoft, as I am already using RestSharp.
RestSharp removed JSON.NET support in v103.0. The default Json Serializer is no longer compatible with Json.NET. You have a couple of options if you want to continue using JSON.NET and maintain backwards compatibility. Beyond that, JSON.NET has more capability and may solve your issues over using the basic .NET serializer which RestSharp now depends on.
Plus you can use the [EnumMember] properties to define custom mappings during deserialization.
Option 1: Implement a custom serializer that uses JSON.NET
To use Json.NET for serialization, copy this code:
/// <summary>
/// Default JSON serializer for request bodies
/// Doesn't currently use the SerializeAs attribute, defers to Newtonsoft's attributes
/// </summary>
public class JsonNetSerializer : ISerializer
{
private readonly Newtonsoft.Json.JsonSerializer _serializer;
/// <summary>
/// Default serializer
/// </summary>
public JsonSerializer() {
ContentType = "application/json";
_serializer = new Newtonsoft.Json.JsonSerializer {
MissingMemberHandling = MissingMemberHandling.Ignore,
NullValueHandling = NullValueHandling.Include,
DefaultValueHandling = DefaultValueHandling.Include
};
}
/// <summary>
/// Default serializer with overload for allowing custom Json.NET settings
/// </summary>
public JsonSerializer(Newtonsoft.Json.JsonSerializer serializer){
ContentType = "application/json";
_serializer = serializer;
}
/// <summary>
/// Serialize the object as JSON
/// </summary>
/// <param name="obj">Object to serialize</param>
/// <returns>JSON as String</returns>
public string Serialize(object obj) {
using (var stringWriter = new StringWriter()) {
using (var jsonTextWriter = new JsonTextWriter(stringWriter)) {
jsonTextWriter.Formatting = Formatting.Indented;
jsonTextWriter.QuoteChar = '"';
_serializer.Serialize(jsonTextWriter, obj);
var result = stringWriter.ToString();
return result;
}
}
}
/// <summary>
/// Unused for JSON Serialization
/// </summary>
public string DateFormat { get; set; }
/// <summary>
/// Unused for JSON Serialization
/// </summary>
public string RootElement { get; set; }
/// <summary>
/// Unused for JSON Serialization
/// </summary>
public string Namespace { get; set; }
/// <summary>
/// Content type for serialized content
/// </summary>
public string ContentType { get; set; }
}
and register it with your client:
var client = new RestClient();
client.JsonSerializer = new JsonNetSerializer();
Option 2: Use a nuget package to use JSON.NET
Instead of doing all of that and having a custom JSON serializer spread throughout your projects is to just use this nuget package: https://www.nuget.org/packages/RestSharp.Newtonsoft.Json. It allows you to use an inherited RestRequest object that defaults to using Newtonsoft.JSON internally like this:
var request = new RestSharp.Newtonsoft.Json.RestRequest(); // Uses JSON.NET
The other option is to set it on every request like this:
var request = new RestRequest();
request.JsonSerializer = new NewtonsoftJsonSerializer();
Disclaimer: I created this project after getting frustrated with having a custom serializer laying around in my projects. I created this to keep things clean and hopefully help others who want backwards compatibility with their RestSharp code that worked prior to v103.
Found solution for this problem:
private IRestClient GetRestClient()
{
return new RestClient(url)
.AddDefaultHeader("Authorization", $"Bearer {token.AccessToken}")
.AddDefaultHeader("Accept", "*/*")
.AddDefaultHeader("Accept-Encoding", "gzip, deflate, br")
.AddDefaultHeader("Connection", "close")
.UseSystemTextJson(new JsonSerializerOptions
{
Converters = { new JsonStringEnumConverter() }
});
}
I.e. instructed RestSharp to use System.Text.Json serializer and then instructed the serializer to use JsonStringEnumConverter class to serialize/deserialize enums.
I got it working with the help of PocoJsonSerializerStrategy. RestSharp allows you to specify your own serialization/deserialization strategy, so I created my own strategy that handles enums for me:
public class HandleEnumsJsonSerializerStrategy : PocoJsonSerializerStrategy
{
public override object DeserializeObject(object value, Type type)
{
if (type.IsEnum)
return Enum.Parse(type, (string)value);
else
return base.DeserializeObject(value, type);
}
}
Now you can pass an object of this class to SimpleJson.DeserializeObject call, like this and your enums are handled elegantly:
SimpleJson.DeserializeObject<JsonObject>(Response.Content, Strategy);
If you are using Enumeration class pattern you can user a JsonConverter approach. It means you need to create a custom converter and use JsonConverter attribute on the property you need to serialize. Example is following:
using Newtonsoft.Json;
using System;
namespace MyAwesomeAPI
{
public class EnumerationConverter<TEnum> : JsonConverter
where TEnum : Enumeration
{
public override bool CanConvert(Type objectType) => objectType == typeof(TEnum);
public override bool CanRead { get => true; }
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var uknown = Enumeration.FromDisplayName<TEnum>("Unknown");
if (reader.TokenType == JsonToken.Null)
{
return null;
}
if (reader.TokenType == JsonToken.String)
{
var enumText = reader.Value?.ToString();
if (string.IsNullOrEmpty(enumText))
{
return uknown;
}
try
{
return Enumeration.FromDisplayName<TEnum>(enumText);
}
catch (Exception)
{
return uknown;
}
}
return uknown;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteValue(value.ToString());
}
}
}
Where our enum looks something like:
public class CardType : Enumeration
{
public static CardType Unknown = new(0, nameof(Unknown));
public static CardType Amex = new(1, nameof(Amex));
public static CardType Visa = new(2, nameof(Visa));
public static CardType MasterCard = new(3, nameof(MasterCard));
public CardType(int id, string name) : base(id, name)
{
}
}
And last thing is apply the attribute to a property
public class CreditCardResponse
{
[JsonProperty("card_number")]
public string CardNumber { get; set; }
[JsonProperty("card_type")]
[JsonConverter(typeof(EnumerationConverter<CardType>))]
public CardType CardType { get; set; }
}

Custom JSON Derivative Format

I would like to have a serialization format that is nearly identical to JSON, except that key-values are represented as <key>="<value>" instead of "<key>":"<value>".
With Newtonsoft I made a custom JsonConverter called TsonConverter that works fairly well, except that it can't "see" an embedded dictionary. Given the following type:
public class TraceyData
{
[Safe]
public string Application { get; set; }
[Safe]
public string SessionID { get; set; }
[Safe]
public string TraceID { get; set; }
[Safe]
public string Workflow { get; set; }
[Safe]
public Dictionary<string, string> Tags {get; set; }
[Safe]
public string[] Stuff {get; set;}
}
And the following code:
TsonConverter weird = new TsonConverter();
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.NullValueHandling = NullValueHandling.Ignore;
settings.Converters.Add(weird);
var tracey = new TraceyData();
tracey.TraceID = Guid.NewGuid().ToString();
tracey.SessionID = "5";
tracey.Tags["Referrer"] = "http://www.sky.net/deals";
tracey.Stuff = new string[] { "Alpha", "Bravo", "Charlie" };
tracey.Application = "Responsive";
string stuff = JsonConvert.SerializeObject(tracey, settings);
I get this:
[Application="Responsive" SessionID="5" TraceID="082ef853-92f8-4ce8-9f32-8e4f792fb022" Tags={"Referrer":"http://www.sky.net/deals"} Stuff=["Alpha","Bravo","Charlie"]]
Obviously I have also overridden the StartObject/EndObject notation, replacing { } with [ ]. Otherwise the results are not bad.
However, there is still the problem of the internal dictionary. In order
to convert the dictionary as well to use my <key>="<value>" format, it looks like I must make a deep dictionary converter.
I'm wondering if there is an easier way to do this.
Perhaps the Newtonsoft tool has a "property generator" and "key-value" generator property that I can set that globally handles this for me?
Any suggestions?
And while we're here, I wonder if there is a StartObject/EndObject formatter property override I can set, which would handle the other customization I've shown above. It would be nice to "skip" making JsonConverter tools for these kinds of simple alterations.
Incidentally:
My custom JsonConverter is choosing properties to serialize based on the [Safe] attribute shown in my sample. This is another nice-to-have. It would be wonderful if the JSon settings could expose an "attribute handler" property that lets me override the usual JSon attributes in favor of my own.
I have no need to de-serialize this format. It is intended as a one-way operation. If someone wishes also to explain how to de-serialize my custom format as well that is an interesting bonus, but definitely not necessary to answer this question.
Appendix
Below is the TraceConverter I had made. It references a FieldMetaData class that simply holds property info.
public class TsonConverter : JsonConverter
{
public override bool CanRead
{
get
{
return false;
}
}
public override bool CanConvert(Type ObjectType)
{
return DataClassifier.TestForUserType(ObjectType);
}
public override void WriteJson(
JsonWriter writer, object value, JsonSerializer serializer)
{
Type objType = value.GetType();
var props = objType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
var propMap = from p in props
from a in p.GetCustomAttributes(typeof(ProfileAttribute), false)
select new FieldMetaData(p, (ProfileAttribute)a);
//writer.WriteStartObject();
writer.WriteStartArray();
bool loopStarted = true;
foreach(var prop in propMap){
object rawValue = prop.GetValue(value);
if (rawValue != null || serializer.NullValueHandling == NullValueHandling.Include)
{
string jsonValue = JsonConvert.SerializeObject(prop.GetValue(value), this);
if (loopStarted)
{
loopStarted = false;
writer.WriteRaw(String.Format("{0}={1}", prop.Name, jsonValue));
}
else
{
writer.WriteRaw(String.Format(" {0}={1}", prop.Name, jsonValue));
}
}
//writer.WriteRaw(String.Format("{0}={1}", prop.Name, prop.GetValue(value)));
//writer.WritePropertyName(prop.Name, false);
//writer.WriteValue(prop.GetValue(value));
}
writer.WriteEndArray();
}
public override object ReadJson(
JsonReader reader, Type objectType,
object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Rather than creating your own converter, you're going to need to create your own subclass of JsonWriter that writes to your custom file format. (This is how Json.NET implements its BsonWriter.) In your case, your file format is close enough to JSON that you can inherit from JsonTextWriter:
public class TsonTextWriter : JsonTextWriter
{
TextWriter _writer;
public TsonTextWriter(TextWriter textWriter)
: base(textWriter)
{
if (textWriter == null)
throw new ArgumentNullException("textWriter");
QuoteName = false;
_writer = textWriter;
}
public override void WriteStartObject()
{
SetWriteState(JsonToken.StartObject, null);
_writer.Write('[');
}
protected override void WriteEnd(JsonToken token)
{
switch (token)
{
case JsonToken.EndObject:
_writer.Write(']');
break;
default:
base.WriteEnd(token);
break;
}
}
public override void WritePropertyName(string name)
{
WritePropertyName(name, true);
}
public override void WritePropertyName(string name, bool escape)
{
SetWriteState(JsonToken.PropertyName, name);
var escaped = name;
if (escape)
{
escaped = JsonConvert.ToString(name, '"', StringEscapeHandling);
escaped = escaped.Substring(1, escaped.Length - 2);
}
// Maybe also escape the space character if it appears in a name?
_writer.Write(escaped.Replace("=", #"\u003d"));// Replace "=" with unicode escape sequence.
_writer.Write('=');
}
/// <summary>
/// Writes the JSON value delimiter. (Remove this override if you want to retain the comma separator.)
/// </summary>
protected override void WriteValueDelimiter()
{
_writer.Write(' ');
}
/// <summary>
/// Writes an indent space.
/// </summary>
protected override void WriteIndentSpace()
{
// Do nothing.
}
}
Having done this, now all classes will be serialized to your custom format when you use this writer, for instance:
var tracey = new TraceyData();
tracey.TraceID = Guid.NewGuid().ToString();
tracey.SessionID = "5";
tracey.Tags["Referrer"] = "http://www.sky.net/deals";
tracey.Stuff = new string[] { "Alpha", "Bravo", "Charlie" };
tracey.Application = "Responsive";
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.NullValueHandling = NullValueHandling.Ignore;
using (var sw = new StringWriter())
{
using (var jsonWriter = new TsonTextWriter(sw))
{
JsonSerializer.CreateDefault(settings).Serialize(jsonWriter, tracey);
}
Debug.WriteLine(sw.ToString());
}
Produces the output
[Application="Responsive" SessionID="5" TraceID="2437fe67-9788-47ba-91ce-2e5b670c2a34" Tags=[Referrer="http://www.sky.net/deals"] Stuff=["Alpha" "Bravo" "Charlie"]]
As far as deciding whether to serialize properties based on the presence of a [Safe] attribute, that's sort of a second question. You will need to create your own ContractResolver and override CreateProperty, for instance as shown here: Using JSON.net, how do I prevent serializing properties of a derived class, when used in a base class context?
Update
If you want to retain the comma separator for arrays but not objects, modify WriteValueDelimiter as follows:
/// <summary>
/// Writes the JSON value delimiter. (Remove this override if you want to retain the comma separator.)
/// </summary>
protected override void WriteValueDelimiter()
{
if (WriteState == WriteState.Array)
_writer.Write(',');
else
_writer.Write(' ');
}

How to parse JSON without JSON.NET library?

I'm trying to build a Metro application for Windows 8 on Visual Studio 2011.
and while I'm trying to do that, I'm having some issues on how to parse JSON without JSON.NET library (It doesn't support the metro applications yet).
Anyway, I want to parse this:
{
"name":"Prince Charming",
"artist":"Metallica",
"genre":"Rock and Metal",
"album":"Reload",
"album_image":"http:\/\/up203.siz.co.il\/up2\/u2zzzw4mjayz.png",
"link":"http:\/\/f2h.co.il\/7779182246886"
}
You can use the classes found in the System.Json Namespace which were added in .NET 4.5. You need to add a reference to the System.Runtime.Serialization assembly
The JsonValue.Parse() Method parses JSON text and returns a JsonValue:
JsonValue value = JsonValue.Parse(#"{ ""name"":""Prince Charming"", ...");
If you pass a string with a JSON object, you should be able to cast the value to a JsonObject:
using System.Json;
JsonObject result = value as JsonObject;
Console.WriteLine("Name .... {0}", (string)result["name"]);
Console.WriteLine("Artist .. {0}", (string)result["artist"]);
Console.WriteLine("Genre ... {0}", (string)result["genre"]);
Console.WriteLine("Album ... {0}", (string)result["album"]);
The classes are quite similar to those found in the System.Xml.Linq Namespace.
I use this...but have never done any metro app development, so I don't know of any restrictions on libraries available to you. (note, you'll need to mark your classes as with DataContract and DataMember attributes)
public static class JSONSerializer<TType> where TType : class
{
/// <summary>
/// Serializes an object to JSON
/// </summary>
public static string Serialize(TType instance)
{
var serializer = new DataContractJsonSerializer(typeof(TType));
using (var stream = new MemoryStream())
{
serializer.WriteObject(stream, instance);
return Encoding.Default.GetString(stream.ToArray());
}
}
/// <summary>
/// DeSerializes an object from JSON
/// </summary>
public static TType DeSerialize(string json)
{
using (var stream = new MemoryStream(Encoding.Default.GetBytes(json)))
{
var serializer = new DataContractJsonSerializer(typeof(TType));
return serializer.ReadObject(stream) as TType;
}
}
}
So, if you had a class like this...
[DataContract]
public class MusicInfo
{
[DataMember]
public string Name { get; set; }
[DataMember]
public string Artist { get; set; }
[DataMember]
public string Genre { get; set; }
[DataMember]
public string Album { get; set; }
[DataMember]
public string AlbumImage { get; set; }
[DataMember]
public string Link { get; set; }
}
Then you would use it like this...
var musicInfo = new MusicInfo
{
Name = "Prince Charming",
Artist = "Metallica",
Genre = "Rock and Metal",
Album = "Reload",
AlbumImage = "http://up203.siz.co.il/up2/u2zzzw4mjayz.png",
Link = "http://f2h.co.il/7779182246886"
};
// This will produce a JSON String
var serialized = JSONSerializer<MusicInfo>.Serialize(musicInfo);
// This will produce a copy of the instance you created earlier
var deserialized = JSONSerializer<MusicInfo>.DeSerialize(serialized);
For those who do not have 4.5, Here is my library function that reads json. It requires a project reference to System.Web.Extensions.
using System.Web.Script.Serialization;
public object DeserializeJson<T>(string Json)
{
JavaScriptSerializer JavaScriptSerializer = new JavaScriptSerializer();
return JavaScriptSerializer.Deserialize<T>(Json);
}
Usually, json is written out based on a contract. That contract can and usually will be codified in a class (T). Sometimes you can take a word from the json and search the object browser to find that type.
Example usage:
Given the json
{"logEntries":[],"value":"My Code","text":"My Text","enabled":true,"checkedIndices":[],"checkedItemsTextOverflows":false}
You could parse it into a RadComboBoxClientState object like this:
string ClientStateJson = Page.Request.Form("ReportGrid1_cboReportType_ClientState");
RadComboBoxClientState RadComboBoxClientState = DeserializeJson<RadComboBoxClientState>(ClientStateJson);
return RadComboBoxClientState.Value;
I needed a JSON serializer and deserializer without any 3rd party dependency or nuget, that can support old systems, so you don't have to choose between Newtonsoft.Json, System.Text.Json, DataContractSerializer, JavaScriptSerializer, etc. depending on the target platform.
So I have started this open source (MIT) project here:
https://github.com/smourier/ZeroDepJson
It's just one C# file ZeroDepJson.cs, compatible with .NET Framework 4.x to .NET Core, and .NET 5.
Note it's probably not as good as all the aforementioned libraries (especially in performance area), but it should be reasonably ok and friction-free.
Have you tried using JavaScriptSerializer ?
There's also DataContractJsonSerializer
I have written a small construct to do that long back, it requires System.Runtime.Serialization.Json namespace. It uses DataContractJsonSerializer to serialize & deserialize object with a static method JConvert.
It works with small set of data but haven't tested with big data source.
JsonHelper.cs
// Json Serializer without NewtonSoft
public static class JsonHelper
{
public static R JConvert<P,R>(this P t)
{
if(typeof(P) == typeof(string))
{
var return1 = (R)(JsonDeserializer<R>(t as string));
return return1;
}
else
{
var return2 = (JsonSerializer<P>(t));
R result = (R)Convert.ChangeType(return2, typeof(R));
return result;
}
}
private static String JsonSerializer<T>(T t)
{
var stream1 = new MemoryStream();
var ser = new DataContractJsonSerializer(typeof(T));
ser.WriteObject(stream1, t);
stream1.Position = 0;
var sr = new StreamReader(stream1);
return (sr.ReadToEnd());
}
private static T JsonDeserializer<T>(string jsonString)
{
T deserializedUser = default(T);
var ms = new MemoryStream(Encoding.UTF8.GetBytes(jsonString));
var ser = new DataContractJsonSerializer(typeof(T));
deserializedUser = (T)ser.ReadObject(ms);// as T;
ms.Close();
return deserializedUser;
}
}
Syntax:
To use the JsonHelper you need to call
JConvert<string,object>(str); //to Parse string to non anonymous <object>
JConvert<object,string>(obj); //to convert <obj> to string
Example:
Suppose we have a class person
public class person
{
public string FirstName {get;set;}
public string LastName {get;set;}
}
var obj = new person();//"vinod","srivastav");
obj.FirstName = "vinod";
obj.LastName = "srivastav";
To convert the person object we can call:
var asText = JsonHelper.JConvert<person,string>(obj); //to convert <obj> to string
var asObject = JsonHelper.JConvert<string,person>(asText); //to convert string to non-anonymous object
You can use DataContractJsonSerializer. See this link for more details.
using System;
using System.IO;
using System.Runtime.Serialization.Json;
using System.Text;
namespace OTL
{
/// <summary>
/// Before usage: Define your class, sample:
/// [DataContract]
///public class MusicInfo
///{
/// [DataMember(Name="music_name")]
/// public string Name { get; set; }
/// [DataMember]
/// public string Artist{get; set;}
///}
/// </summary>
/// <typeparam name="T"></typeparam>
public class OTLJSON<T> where T : class
{
/// <summary>
/// Serializes an object to JSON
/// Usage: string serialized = OTLJSON<MusicInfo>.Serialize(musicInfo);
/// </summary>
/// <param name="instance"></param>
/// <returns></returns>
public static string Serialize(T instance)
{
var serializer = new DataContractJsonSerializer(typeof(T));
using (var stream = new MemoryStream())
{
serializer.WriteObject(stream, instance);
return Encoding.Default.GetString(stream.ToArray());
}
}
/// <summary>
/// DeSerializes an object from JSON
/// Usage: MusicInfo deserialized = OTLJSON<MusicInfo>.Deserialize(json);
/// </summary>
/// <param name="json"></param>
/// <returns></returns>
public static T Deserialize(string json)
{
if (string.IsNullOrEmpty(json))
throw new Exception("Json can't empty");
else
try
{
using (var stream = new MemoryStream(Encoding.Default.GetBytes(json)))
{
var serializer = new DataContractJsonSerializer(typeof(T));
return serializer.ReadObject(stream) as T;
}
}
catch (Exception e)
{
throw new Exception("Json can't convert to Object because it isn't correct format.");
}
}
}
}

Categories

Resources