我使用的是: 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来获得正确的结果?(我正在搜索有关这个问题的信息(所以[json.net]、JSON.NET文档和谷歌),但没有发现任何有用的信息。)
已更新
因此,我被选中使用TypeNameHandling参数的选项(All,数组,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格式的文章,并与源代码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来解决我原来的问题。(下面是一个我的灵魂的借口/咒语,他有完美主义的偏好,HA-HA。))
我希望这些信息是有用的。
发布于 2013-11-28 06:35:05
在各种序列化器中,对[Serializable]
、ISerializable
或[DataContract]
等的需求非常普遍。一种常见的行为是序列化/反序列化带有这些标记的所有内容,并在注意到任何未标记的内容时抛出。未标记为可序列化的对象通常被视为“有未知的序列化过程”,因此不能序列化,因此(反)序列化在这种情况下失败--它根本不知道如何处理它们。
从您的观察来看,JSON.Net的行为似乎不同--它只是跳过未知的对象。这是一个遗憾,因为如果它抛出了一个异常,那么它会在异常的消息中给您留下一个提示。例如,通常的解决方案是将有问题的类型添加到由序列化框架管理的“使用典型约定进行安全序列化的知名类型”列表中。我是说,简单的说:
MySerializationLibrary.KnownTypes.Add( typeof(MyUnattributedClass) );
通常都够了。
JSON.Net似乎很高兴地序列化了一个没有属性的类。至少本文是这样声明的:在本文中,您看到只在提供文本的情况下调用一个http://alexandrebrisebois.wordpress.com/2012/06/24/using-json-net-to-serialize-objects/,但同时也看到了确切的对象类型( TType
参数)。这可能会迫使库序列化/反序列化该未属性化的对象,因为您明确地给出了要处理的类型。
所以,很可能JSON.Net也有一些“已知类型”,但我找不到它。
另一件事,请看这里:https://stackoverflow.com/a/6495299/717732没有任何一个对象是属性化的,TypeNameHandling
强制库解释在JSON数据包中写入所有类型名,所以稍后它知道将它们反序列化为什么。也许只是因为你失踪了?
如果您仍然运气不佳,那么可以为该OracleParameter
类提供外部序列化程序。参见这里:http://james.newtonking.com/json/help/index.html?topic=html/SerializeSerializationBinder.htm -- JSON.Net库允许您创建一个特殊的“助手类”,并注册它以处理(反)序列化您选择的某些类型。您可以为该OracleParam
创建并注册该助手,然后“手动”处理它:编写一些代码,检查其中的内容并将其转储到存储区(或从存储区读取它并放入OracleParam对象中)。
编辑:
您甚至可以直接向DeserializeObject法提供自定义转换器。以下是换流器的参考资料。因此,您可以创建CustomCreationConverter<OracleParameter>
实现,并在需要时将其传递给(反)序列化程序。
发布于 2014-01-02 00:33:34
您可能想检查一下我添加到这个单元测试中(查找:"$type"):
https://github.com/ysharplanguage/FastJsonParser/blob/master/JsonTest/Program.cs
下面是我提到的单元测试摘录,非常类似于您的情况(模块化类型名称):
// Support for JSON.NET's "$type" pseudo-key (in addition to ServiceStack's "__type"):
Person jsonNetPerson = new Person { Id = 123, Name = "Foo", Scores = new[] { 100, 200, 300 } };
// (Expected serialized form shown in next comment)
string jsonNetString = JsonConvert.SerializeObject(jsonNetPerson, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Objects });
// => '{"$type":"Test.Program+Person, Test","Id":123,"Name":"Foo","Status":0,"Address":null,"Scores":[100,200,300],"Data":null,"History":null}'
// (Note the Parse<object>(...))
object restoredObject = UnitTest(jsonNetString, s => new JsonParser().Parse<object>(jsonNetString));
System.Diagnostics.Debug.Assert
(
restoredObject is Person &&
((Person)restoredObject).Name == "Foo" &&
((IList<int>)((Person)restoredObject).Scores).Count == 3 &&
((IList<int>)((Person)restoredObject).Scores)[2] == 300
);
因此,在序列化期间让JSON.NET在伪键"$type“中发出对象类型信息(即完全限定的类型名称)没有任何问题,这要归功于上面的内容:
new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Objects }
现在,我不能讨论如何正确地反序列化您的OracleParameter,因为我不能单独测试它,但是上面的这个单元测试展示了如何通过将该类型的信息提示放入"$type“伪键中来实现JSON.NET序列化,以及如何反序列化(通过这个可能替代微软JavaScriptSerializer的方法)而不要求您在任何地方添加自定义属性(这是不可能的,在您没有编写的单独编译模块上)。
希望这能帮上忙
https://stackoverflow.com/questions/20267601
复制相似问题