我使用:C#,. NET 2.0和JSON.NET v5.08.16617。
我为Oracle DB编写了CRUD接口,并使用DNF格式子句加入了搜索过滤器。 下一步,我写了一个函数来验证用户数据(这不是关于特殊符号的转义,以避免SQL注入,而是验证字段的名称)。 在这个函数中,我使用了像Dictionary这样的哈希表。我希望将它序列化为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
}
如您所见,结果包含较少的字段(仅包含在查询中的字段)和参数Value(byte [])转换为字符串。可以为OracleParameterSerializer类编写反序列化方法 - 但我没有看到这一点,因为无论如何我的自定义转换器都没有自动连接。 也许有一种方法可以“修补”标准的OracleParameter,添加所需的属性或编写一个类SerializableOracleParameter,从System.Data.Common.DbParameter继承它,以及转换方法就像一个 ConvertMethod(SerializableOracleParameter) - &gt;的OracleParameter。但是做这样的事情需要一个很好的理由。
因此,我决定将所有内容保留原样并使用JavaScriptSerializer来解决原始问题。 (下面是我灵魂部分的借口/口头禅,有完美主义偏好,HA-HA。)
我希望这些信息有用。
答案 0 :(得分:0)
[Serializable]
,ISerializable
或[DataContract]
等的要求在各种序列化程序中非常常见。一种常见的行为是序列化/反序列化用这些标记的所有内容,并注意到任何未标记的内容。未标记为可序列化的对象通常被视为“具有未知的序列化过程”,因此不可序列化,因此(de)序列化在这种情况下失败 - 它根本不知道如何处理它们。
根据您的观察,JSON.Net似乎行为不同 - 它只是跳过未知对象。这是一个遗憾,因为如果它抛出一个异常,那么它可以给你一个暗示在异常消息中做什么。例如,通常的解决方案是将有问题的类型添加到序列化框架管理的某些“使用典型约定可安全序列化的众所周知的类型”列表中。我的意思是,简单地说:
MySerializationLibrary.KnownTypes.Add( typeof(MyUnattributedClass) );
通常就足够了。
JSON.Net似乎很乐意序列化一个未归属的类。至少这篇文章声称如此:http://alexandrebrisebois.wordpress.com/2012/06/24/using-json-net-to-serialize-objects/在本文中,您会看到仅在提供文本的情况下调用Deserialize
,而且还有确切的对象类型(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库允许您创建一个特殊的“帮助程序类”并将其注册以处理(de)某些类型的序列化。您可以为OracleParam
创建并注册这样的帮助程序,然后“手动”处理它:编写一些代码来检查内部的内容并将其转储到商店(或从商店读取并放入OracleParam对象)。 / p>
编辑:
您甚至可以直接向DeserializeObject method提供自定义转换器。这是converters的参考。因此,您可以创建CustomCreationConverter<OracleParameter>
实现,并在需要时将其传递给(de)序列化程序。
答案 1 :(得分:0)
您可能想查看我添加到此单元的单元测试(查找:“$ 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,因为我不能自己测试它,但上面的单元测试显示了如何通过放置类型信息提示来使JSON.NET序列化进入“$ type”伪键,以及如何反序列化(通过这可能替代Microsoft的JavaScriptSerializer),而不需要在任何地方添加自定义属性(这是不可能的,在你没有编写的Oracle单独编译的模块上)。
'希望这有帮助,