I work with: C#, .NET 2.0 and JSON.NET v5.08.16617.
I wrote CRUD-interface to Oracle DB and joined search-filter for it with the DNF-format clauses. Next step, I wrote a function to validate user data ( This is not about escape of special symbols to avoid SQL injections, but the validation of field's names ) . In this function I used a hash-table like a Dictionary. I hoped to serialize it into JSON format and put it into the resource file - with the aim of getting access to it if I need and sometimes make some changes without recompiling the whole project again.
For this purpose I used JSON.NET library and saw a problem: some objects are not serialized / deserialized with JSON.NET, for example - OracleParameter.
My test code:
string vJsonStr;
Dictionary<string, OracleParameter> vDictionary = new Dictionary<string, OracleParameter> ();
OracleParameter vOp;
vOp = new OracleParameter();
vOp.DbType = DbType.String;
vOp.OracleType = OracleType.VarChar;
vOp.Value = "qwerty";
vOp.Direction = ParameterDirection.InputOutput;
vDictionary.Add("p1", vOp);
vOp = new OracleParameter();
vOp.OracleType = OracleType.Clob;
vOp.Value = new byte[3] { 1, 2, 3 };
vOp.Direction = ParameterDirection.Input;
vDictionary.Add("p2", vOp);
vJsonStr = JsonConvert.SerializeObject(vDictionary);
And the result (bad):
{
"p1": "",
"p2": ""
}
As a temporary and quick solution I used the JavaScriptSerializer.
My test code:
JavaScriptSerializer javaScriptSerializer = new JavaScriptSerializer();
vJsonStr = javaScriptSerializer.Serialize(vDictionary);
And the result (great):
{
"p1": {
"DbType": 0,
"OracleType": 22,
"ParameterName": "",
"Precision": 0,
"Scale": 0,
"Value": "qwerty",
"Direction": 3,
"IsNullable": false,
"Offset": 0,
"Size": 6,
"SourceColumn": "",
"SourceColumnNullMapping": false,
"SourceVersion": 512
},
"p2": {
"DbType": 0,
"OracleType": 4,
"ParameterName": "",
"Precision": 0,
"Scale": 0,
"Value": [
1,
2,
3
],
"Direction": 1,
"IsNullable": false,
"Offset": 0,
"Size": 3,
"SourceColumn": "",
"SourceColumnNullMapping": false,
"SourceVersion": 512
}
}
Deserialization is worked funny too:
Dictionary<string, OracleParameter> test2 = javaScriptSerializer.Deserialize<Dictionary<string, OracleParameter>>(vJsonStr);
This solution is stable and very fast for me, but I got an extra link on JavaScriptSerializer.
So my question is: how can I get the correct result using the JSON.NET library instead of JavaScriptSerializer? (A course I was searching the information about this issue (SO [json.net] and JSON.NET documentation and google), but I did not find anything useful.)
UPDATED
And so, I'm checked the option to use TypeNameHandling parameter (All, Arrays, Auto, None, Objects) - it does not work for me.
For example, code like
var vSettings = new JsonSerializerSettings();
vSettings.TypeNameHandling = TypeNameHandling.Objects;
vJsonStr = JsonConvert.SerializeObject(vDictionary, Formatting.Indented, vSettings);
only adds parameter $type to serialized string:
{
"$type": "System.Collections.Generic.Dictionary`2[[System.String, mscorlib],[System.Data.OracleClient.OracleParameter, System.Data.OracleClient]], mscorlib",
"p1": "",
"p2": ""
}
Ok, I've checked theme about the custom converters. I've found several articles in howto-format and I checked with source JSON.NET also: it contains a template for new converters - an abstract class CustomCreationConverter ( The rest of the code, though a structured and well commented, but for me it takes much time to understand).
Nevertheless, I wrote a small prototype to test my assumptions:
public class OracleParameterSerializer: JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var vOp = value as OracleParameter;
writer.WriteStartObject();
writer.WritePropertyName("DbType");
serializer.Serialize(writer, vOp.DbType);
writer.WritePropertyName("Direction");
serializer.Serialize(writer, vOp.Direction);
writer.WritePropertyName("IsNullable");
serializer.Serialize(writer, vOp.IsNullable);
writer.WritePropertyName("Offset");
serializer.Serialize(writer, vOp.Offset);
writer.WritePropertyName("OracleType");
serializer.Serialize(writer, vOp.OracleType);
writer.WritePropertyName("ParameterName");
serializer.Serialize(writer, vOp.ParameterName);
writer.WritePropertyName("Size");
serializer.Serialize(writer, vOp.Size);
writer.WritePropertyName("SourceColumn");
serializer.Serialize(writer, vOp.SourceColumn);
writer.WritePropertyName("SourceColumnNullMapping");
serializer.Serialize(writer, vOp.SourceColumnNullMapping);
writer.WritePropertyName("SourceVersion");
serializer.Serialize(writer, vOp.SourceVersion);
writer.WritePropertyName("Value");
serializer.Serialize(writer, vOp.Value);
writer.WriteEndObject();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override bool CanConvert(Type objectType)
{
return typeof(OracleParameter).IsAssignableFrom(objectType);
}
}
The main problem here is the addition of an attribute to the class that I'm want to serialize:
[JsonConverter(typeof(OracleParameterSerializer))]
...Class OracleParameter...
But OracleParameter - already assembled and I can not to change its attributes. However, I found one solution with using System.ComponentModel (Attributes are added at runtime):
var vAttrs1 = TypeDescriptor.GetAttributes(typeof(OracleParameter));
TypeDescriptor.AddAttributes(typeof(OracleParameter), new Attribute[] { new JsonConverterAttribute(typeof(OracleParameterSerializer)) }); // JsonConverter(typeof(OracleParameterSerializer)) - it's not working, I don't know why.
var vAttrs2 = TypeDescriptor.GetAttributes(typeof(OracleParameter)); // Added [Newtonsoft.Json.JsonConverterAttribute]
Although it does not work (I.e., the attribute is added - but serialization fails), I've seen that OracleParameter has an attribute [System.SerializableAttribute] - apparently, it allows the standard JavaScriptSerializer to serialize this class.
Okay, I've tried the directly serialization ( I serialize OracleParameter "p2" ) :
vJsonStr = JsonConvert.SerializeObject(vOp, Formatting.Indented, new OracleParameterSerializer());
it gets something like:
{
"DbType": 0,
"OracleType": 4,
"ParameterName": "",
"Value": "AQID",
"Direction": 1,
"IsNullable": false,
"Offset": 0,
"Size": 3,
"SourceColumn": "",
"SourceColumnNullMapping": false,
"SourceVersion": 512
}
As you can see, the result contains the less fields ( only those that I have included in the query ) and parameter Value (byte []) converted to string. It's possible to write a deserialize method for OracleParameterSerializer class - but I don't see the point, because my custom converter is not joined automatically anyway. Perhaps there is a way "to patch" standard OracleParameter, adding the required attributes or write a class SerializableOracleParameter, inheriting it from System.Data.Common.DbParameter, as well as the converting method like a ConvertMethod (SerializableOracleParameter) -> OracleParameter. But it needs a good reason to do something like that.
Thus, I decided to leave everything as it is and use the JavaScriptSerializer for my original problem. ( Below is an excuse/mantra for a my soul-part, who has perfectionistic preferences, HA-HA. )
- My application has a good performance at this point at the moment.
- I have already been using JavaScriptSerializer in some part of my code (Since JSON.NET categorically does not fit.).
- Handling JavaScriptSerializer is trivial.
I hope this information was useful.