首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >问答首页 >用于嵌入式严格类型.NET对象的JSON序列化和反序列化

用于嵌入式严格类型.NET对象的JSON序列化和反序列化
EN

Stack Overflow用户
提问于 2013-11-28 05:34:43
回答 2查看 4.7K关注 0票数 3

我使用的是: C#、.NET 2.0和JSON.NET v5.08.16617。

我为Oracle DB编写了CRUD接口,并加入了DNF格式子句的搜索筛选器。下一步,我编写了一个函数来验证用户数据(这不是为了避免SQL注入而对特殊符号进行转义,而是对字段名的验证)。在这个函数中,我使用了像字典一样的散列表。我希望将其序列化为JSON格式,并将其放入资源文件中--目的是在需要的情况下访问它,有时还会进行一些更改,而不会再次重新编译整个项目。

为此目的,我使用了JSON.NET库,并发现了一个问题:一些对象没有用JSON.NET序列化/反序列化,例如- OracleParameter。

我的测试代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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);

其结果(不好):

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
{
    "p1": "",
    "p2": ""
}

作为一种临时的快速解决方案,我使用了JavaScriptSerializer。

我的测试代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
JavaScriptSerializer javaScriptSerializer = new JavaScriptSerializer();
vJsonStr = javaScriptSerializer.Serialize(vDictionary);

其结果(很好):

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
{
    "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
    }
}

反序列化也很有趣:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Dictionary<string, OracleParameter> test2 = javaScriptSerializer.Deserialize<Dictionary<string, OracleParameter>>(vJsonStr);

这个解决方案对我来说是稳定和快速的,但是我在JavaScriptSerializer上有了一个额外的链接。

因此,我的问题是:如何使用JSON.NET库而不是JavaScriptSerializer来获得正确的结果?(我正在搜索有关这个问题的信息(所以[json.net]JSON.NET文档谷歌),但没有发现任何有用的信息。)

已更新

因此,我被选中使用TypeNameHandling参数的选项(All,数组,Auto,None,Objects) --它对我不起作用。

例如,这样的代码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var vSettings = new JsonSerializerSettings();
vSettings.TypeNameHandling = TypeNameHandling.Objects;
vJsonStr = JsonConvert.SerializeObject(vDictionary, Formatting.Indented, vSettings);

只将参数$type添加到序列化字符串:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
{
"$type": "System.Collections.Generic.Dictionary`2[[System.String, mscorlib],[System.Data.OracleClient.OracleParameter, System.Data.OracleClient]], mscorlib",
"p1": "",
"p2": ""
}

好的,我已经检查了有关自定义转换器的主题。我找到了几篇使用howto格式的文章,并与源代码JSON.NET进行了核对:它包含一个新转换器的模板--一个抽象类CustomCreationConverter (代码的其余部分,虽然是结构化的,注释得很好,但对我来说,理解起来需要很长时间)。

尽管如此,我还是写了一个小原型来测试我的假设:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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);
    }
}

这里的主要问题是在我想序列化的类中添加一个属性:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[JsonConverter(typeof(OracleParameterSerializer))]
...Class OracleParameter...

但是OracleParameter --已经组装好了,我不能改变它的属性。但是,我找到了一个使用System.ComponentModel的解决方案(在运行时添加了属性):

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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“):

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
vJsonStr = JsonConvert.SerializeObject(vOp, Formatting.Indented, new OracleParameterSerializer());

它得到的东西是:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
{
  "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。))

  1. 目前,我的应用程序具有良好的性能。
  2. 我已经在代码的某些部分使用了JavaScriptSerializer (因为JSON.NET绝对不适合)。
  3. 处理JavaScriptSerializer非常简单。

我希望这些信息是有用的。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2013-11-28 06:35:05

在各种序列化器中,对[Serializable]ISerializable[DataContract]等的需求非常普遍。一种常见的行为是序列化/反序列化带有这些标记的所有内容,并在注意到任何未标记的内容时抛出。未标记为可序列化的对象通常被视为“有未知的序列化过程”,因此不能序列化,因此(反)序列化在这种情况下失败--它根本不知道如何处理它们。

从您的观察来看,JSON.Net的行为似乎不同--它只是跳过未知的对象。这是一个遗憾,因为如果它抛出了一个异常,那么它会在异常的消息中给您留下一个提示。例如,通常的解决方案是将有问题的类型添加到由序列化框架管理的“使用典型约定进行安全序列化的知名类型”列表中。我是说,简单的说:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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>实现,并在需要时将其传递给(反)序列化程序。

票数 0
EN

Stack Overflow用户

发布于 2014-01-02 00:33:34

您可能想检查一下我添加到这个单元测试中(查找:"$type"):

https://github.com/ysharplanguage/FastJsonParser/blob/master/JsonTest/Program.cs

下面是我提到的单元测试摘录,非常类似于您的情况(模块化类型名称):

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
        // 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“中发出对象类型信息(即完全限定的类型名称)没有任何问题,这要归功于上面的内容:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Objects }

现在,我不能讨论如何正确地反序列化您的OracleParameter,因为我不能单独测试它,但是上面的这个单元测试展示了如何通过将该类型的信息提示放入"$type“伪键中来实现JSON.NET序列化,以及如何反序列化(通过这个可能替代微软JavaScriptSerializer的方法)而不要求您在任何地方添加自定义属性(这是不可能的,在您没有编写的单独编译模块上)。

希望这能帮上忙

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/20267601

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文