序列化System.Data.OleDb.OleDbParameter[]不与Newtonsoft.Json一起使用

toiithl6  于 2023-05-23  发布在  其他
关注(0)|答案(1)|浏览(259)

我正在实现客户端软件和数据库之间的抽象层。该软件最初是使用OleDb编写的,并且运行良好。我们已经通过Data组件处理了数据访问,但客户端软件仍然使用基于OleDb的过程调用其过程。准确地说,我们仍然会将System.Data.OleDb.OleDbParameter[]数组传递到Data组件,它会将System.Data.DataSet对象返回给客户端。
我们现在正在编写一个API,作为数据库和数据组件之间的中间层,因此为了尽可能少地影响客户端软件,我们仍然会以完全相同的方式调用其方法。在内部,数据组件将序列化我们传递到JSON中的OleDbParameter[]数组并调用API,这反过来又将返回序列化为JSON的数据集,以便我们的数据组件可以将它们反序列化为System.Data.DataSet,客户端软件不会知道。
这就是我们遇到的一个奇怪的障碍。
在尝试了System.Text.JsonSystem.Web.Script.Serialization之后,我发现这两种方法都不能很好地与DataSet一起工作。在尝试序列化System.Data.DataSet对象时,这两个对象似乎都存在循环引用问题。在这里,NewtonSoft.json来拯救:
使用NewtonSoft.json序列化DataSet很容易:

string sData = string.Empty;
Newtonsoft.Json.JsonSerializerSettings oSet = new Newtonsoft.Json.JsonSerializerSettings();
sData = Newtonsoft.Json.JsonConvert.SerializeObject(value: oDS, type: oDS.GetType(), settings: oSet);

将其反序列化回DataSet也是如此

System.Data.DataSet oDS = null;
oDS = Newtonsoft.Json.JsonConvert.DeserializeObject<System.Data.DataSet>(value: sData);

所以你会觉得这个选择很容易。NewtonSoft.Json是的,没错。除了...这让我对参数有问题

string sParameters = string.Empty;
System.Data.OleDb.OleDbParameter oXML;
oXML = new System.Data.OleDb.OleDbParameter(name: "XML", dataType: System.Data.OleDb.OleDbType.VarWChar, size: -1);
oXML.Value = "NotRelevantToThisQuestion";

System.Data.OleDb.OleDbParameter oTotalRecords;
oTotalRecords = new System.Data.OleDb.OleDbParameter(name: "TotalRecords", dataType: System.Data.OleDb.OleDbType.Integer);
oTotalRecords.Value = 0;
oTotalRecords.Direction = System.Data.ParameterDirection.InputOutput;

System.Data.OleDb.OleDbParameter[] oParms;
oParms = new System.Data.OleDb.OleDbParameter[] {oXML, oTotalRecords };

Newtonsoft.Json.JsonSerializerSettings oSet = new Newtonsoft.Json.JsonSerializerSettings();
sParameters = Newtonsoft.Json.JsonConvert.SerializeObject(value: oParms, type: oParms.GetType(), settings: oSet);

它似乎生成了一个JSON字符串,列出了这两个参数,但它们的属性都不存在。得到的JSON是:

["XML","TotalRecords"]

我可以使用System.Web.Serialization.JavaScriptSerializer让那个程序正常工作

System.Web.Script.Serialization.JavaScriptSerializer oSer;
oSer = new System.Web.Script.Serialization.JavaScriptSerializer();
sParameters = oSer.Serialize(oParms);

这产生:

[{"DbType":16,"OleDbType":202,"ParameterName":"XML","Precision":0,"Scale":0,"Value":"NotRelevantToThisQuestion","Direction":1,"IsNullable":false,"Size":-1,"SourceColumn":"","SourceColumnNullMapping":false,"SourceVersion":512},{"DbType":11,"OleDbType":3,"ParameterName":"TotalRecords","Precision":0,"Scale":0,"Value":0,"Direction":3,"IsNullable":false,"Size":0,"SourceColumn":"","SourceColumnNullMapping":false,"SourceVersion":512}]

在调用之后,Input/Output参数获得一个值,然后我还可以在接收端对该值进行序列化、发送回和反序列化,以允许我读取它的值,以及我已经使用NewtonSoft.json反序列化的DataSet

System.Data.OleDb.OleDbParameter[] oParms;
System.Web.Script.Serialization.JavaScriptSerializer oSer;
oSer = new System.Web.Script.Serialization.JavaScriptSerializer();
oParms = oSer.Deserialize<System.Data.OleDb.OleDbParameter[]>(sParameters);

所以如果你有足够的耐心,读到这里,我可以想象你现在在想什么:“只需使用NewtonSoft.Json作为您的DataSet,使用System.Web.Script.Serialization.JavaScriptSerializer作为您的参数即可。你有什么问题?”
当然可以。但是我不得不用两个不同的包来做一个包应该能处理的工作。说到这里,我终于可以回答我的问题了:

**是否有任何方法可以用一个包同时处理序列化/反序列化需求?只要它被清楚地记录下来,我很乐意使用我已经提到的3个软件包中的任何一个,甚至你可能在这里展示的另一个软件包。

0lvr5msh

0lvr5msh1#

有两个问题我必须解决。第一个问题是如何序列化单个OleDbParameter。对于那个问题,已经有了@dbc指出的解决方案:
Newtonsoft.JSON无法转换具有TypeConverter属性的模型
对于OleDbParameter,我添加了以下内容:

[TypeConverter(typeof(OleDBParameterConverter))]
[JsonConverter(typeof(NoTypeConverterJsonConverter<System.Data.OleDb.OleDbParameter>))]
public class OleDBParameterConverter : TypeConverter
{
    public override bool CanConvertFrom(ITypeDescriptorContext context, System.Type sourceType)
    {
        if (sourceType == typeof(string))
        {
            return true;
        }
        return base.CanConvertFrom(context, sourceType);
    }
    public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
    {
        if (value is string)
        {
            string s = value.ToString();
            //s = s.Replace("\\", "");
            System.Data.OleDb.OleDbParameter[] f = JsonConvert.DeserializeObject<System.Data.OleDb.OleDbParameter[]>(s);
            return f;
        }
        return base.ConvertFrom(context, culture, value);
    }
}

我添加了NoTypeConverterJsonConverter类

public class NoTypeConverterJsonConverter<T> : JsonConverter
{
    static readonly IContractResolver resolver = new NoTypeConverterContractResolver();

    class NoTypeConverterContractResolver : DefaultContractResolver
    {
        protected override JsonContract CreateContract(Type objectType)
        {
            if (typeof(T).IsAssignableFrom(objectType))
            {
                var contract = this.CreateObjectContract(objectType);
                contract.Converter = null; // Also null out the converter to prevent infinite recursion.
                return contract;
            }
            return base.CreateContract(objectType);
        }
    }

    public override bool CanConvert(Type objectType)
    {
        return typeof(T).IsAssignableFrom(objectType);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        return JsonSerializer.CreateDefault(new JsonSerializerSettings { ContractResolver = resolver }).Deserialize(reader, objectType);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        JsonSerializer.CreateDefault(new JsonSerializerSettings { ContractResolver = resolver }).Serialize(writer, value);
    }
}

这允许我序列化单个System.Data.OleDb.OleDbParameter,但我需要序列化它们的数组,我通过定义一个NoTypeConverterJsonConverter<System.Data.OleDb.OleDbParameter[]>来尝试

Newtonsoft.Json.JsonSerializerSettings oJSet = new Newtonsoft.Json.JsonSerializerSettings();
NoTypeConverterJsonConverter<System.Data.OleDb.OleDbParameter[]> oConv = new NoTypeConverterJsonConverter<System.Data.OleDb.OleDbParameter[]>();
oJSet.Converters.Add(oConv);

但是当我尝试使用它时,它导致了一个ArgumentException。
我最终发现可以通过将Array移动到System.Collections.Generic.List来避免这种情况<System.Data.OleDb.OleDbParameter>

System.Collections.Generic.List<System.Data.OleDb.OleDbParameter> oLParms = new System.Collections.Generic.List<System.Data.OleDb.OleDbParameter>();
foreach (System.Data.OleDb.OleDbParameter oParm in oParms)
{
    oLParms.Add(oParm);
}

这个,我能够序列化没有问题

Newtonsoft.Json.JsonSerializerSettings oJSet = new Newtonsoft.Json.JsonSerializerSettings();
NoTypeConverterJsonConverter<System.Data.OleDb.OleDbParameter> oConv = new NoTypeConverterJsonConverter<System.Data.OleDb.OleDbParameter>();
oJSet.Converters.Add(oConv);
sParameters = Newtonsoft.Json.JsonConvert.SerializeObject(value: oLParms, type: oLParms.GetType(), settings: oJSet);

反序列化也很容易:

oLParms = Newtonsoft.Json.JsonConvert.DeserializeObject<System.Collections.Generic.List<System.Data.OleDb.OleDbParameter>>(value: sParameters, settings: oJSet);
oParms = oLParms.ToArray();

非常感谢@dbc的帮助。

相关问题