【发布时间】:2013-11-28 13:34:43
【问题描述】:
我使用:C#、.NET 2.0 和 JSON.NET v5.08.16617。
我为 Oracle DB 编写了 CRUD 接口,并使用 DNF 格式子句为它加入了搜索过滤器。 下一步,我编写了一个函数来验证用户数据(这不是特殊符号的转义以避免 SQL 注入,而是字段名称的验证)。 在这个函数中,我使用了一个像字典一样的哈希表。我希望将其序列化为 JSON 格式并将其放入资源文件中 - 目的是在需要时访问它 有时在不重新编译整个项目的情况下进行一些更改。
为此,我使用了 JSON.NET 库并发现了一个问题:某些对象没有使用 JSON.NET 进行序列化/反序列化,例如 - OracleParameter。
我的测试代码:
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);
而结果(坏的):
{
"p1": "",
"p2": ""
}
作为一个临时快速的解决方案,我使用了 JavaScriptSerializer。
我的测试代码:
JavaScriptSerializer javaScriptSerializer = new JavaScriptSerializer();
vJsonStr = javaScriptSerializer.Serialize(vDictionary);
结果(很好):
{
"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
}
}
反序列化也很有趣:
Dictionary<string, OracleParameter> test2 = javaScriptSerializer.Deserialize<Dictionary<string, OracleParameter>>(vJsonStr);
这个解决方案对我来说很稳定而且速度很快,但我在 JavaScriptSerializer 上有一个额外的链接。
所以我的问题是:如何使用 JSON.NET 库而不是 JavaScriptSerializer 获得正确的结果? (一门课程我正在搜索有关此问题的信息(SO [json.net] 和 JSON.NET documentation 和 google),但我没有找到任何有用的信息。)
更新
因此,我检查了使用 TypeNameHandling 参数(All、Arrays、Auto、None、Objects)的选项 - 它对我不起作用。
例如,类似这样的代码
var vSettings = new JsonSerializerSettings();
vSettings.TypeNameHandling = TypeNameHandling.Objects;
vJsonStr = JsonConvert.SerializeObject(vDictionary, Formatting.Indented, vSettings);
只将参数 $type 添加到序列化字符串中:
{
"$type": "System.Collections.Generic.Dictionary`2[[System.String, mscorlib],[System.Data.OracleClient.OracleParameter, System.Data.OracleClient]], mscorlib",
"p1": "",
"p2": ""
}
好的,我已经检查了有关自定义转换器的主题。我在 howto-format 中找到了几篇文章,并且我还检查了源 JSON.NET:它包含一个新转换器的模板 - 一个抽象类 CustomCreationConverter (其余的代码,虽然结构清晰,注释很好,但对我来说需要很长时间才能理解)。
尽管如此,我还是写了一个小原型来测试我的假设:
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);
}
}
这里的主要问题是向我要序列化的类添加一个属性:
[JsonConverter(typeof(OracleParameterSerializer))]
...Class OracleParameter...
但是 OracleParameter - 已经组装并且我不能改变它的属性。但是,我找到了一种使用 System.ComponentModel 的解决方案(在运行时添加属性):
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]
虽然它不起作用(即添加了属性 - 但序列化失败),但我看到 OracleParameter 有一个属性 [System.SerializableAttribute] - 显然,它允许标准的 JavaScriptSerializer 序列化这个类。
好的,我试过直接序列化(我序列化OracleParameter“p2”):
vJsonStr = JsonConvert.SerializeObject(vOp, Formatting.Indented, new OracleParameterSerializer());
它得到类似的东西:
{
"DbType": 0,
"OracleType": 4,
"ParameterName": "",
"Value": "AQID",
"Direction": 1,
"IsNullable": false,
"Offset": 0,
"Size": 3,
"SourceColumn": "",
"SourceColumnNullMapping": false,
"SourceVersion": 512
}
如您所见,结果包含较少的字段(仅包含我在查询中包含的那些)和转换为字符串的参数值(字节 [])。可以为 OracleParameterSerializer 类编写反序列化方法 - 但我看不出重点,因为我的自定义转换器无论如何都不会自动加入。 也许有办法“修补”标准 OracleParameter,添加所需的属性或编写一个类 SerializableOracleParameter,从 System.Data.Common.DbParameter 继承它,以及转换方法,如 ConvertMethod (SerializableOracleParameter) -> OracleParameter。但这样做需要一个充分的理由。
因此,我决定保留一切原样并使用 JavaScriptSerializer 来解决我最初的问题。 (下面是我的灵魂部分的借口/口头禅,他有完美主义偏好,哈哈。)
- 我的应用程序在这一点上具有良好的性能 片刻。
- 我已经在某些地方使用了 JavaScriptSerializer 我的代码的一部分(因为 JSON.NET 绝对不适合。)。
- 处理 JavaScriptSerializer 很简单。
我希望这些信息有用。
【问题讨论】:
-
是
OracleParameter[serializable]还是[datacontract]? -
我不知道 - 可能不是:OracleParameter 是一个“嵌入式”类:System.Data.OracleClient.OracleParameter - 我没有创建它。如何将这些属性附加到 OracleParameter?
-
不可能。一旦一个类被编译成一个程序集,属性就会被烧入其中,您既不能删除,也不能添加,也不能更改它们。但是可以在 JSON.Net 中为该类注册一个外部序列化策略,我现在正在检查它。
标签: c# .net json serialization json.net