微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何使用 ServiceStack 将 POCO 存储到具有复杂类型对象和结构作为 JSON 的 MariaDB?

如何解决如何使用 ServiceStack 将 POCO 存储到具有复杂类型对象和结构作为 JSON 的 MariaDB?

我有以下设置:C#、ServiceStack、MariaDB、带有对象和结构的 POCO、JSON。

主要问题是:如何使用 ServiceStack 将 POCO 存储到具有复杂类型(对象和结构)作为 JSON 的 MariaDB 并且仍然可以对相同的 POCO 进行反/序列化?所有这些单个任务都受支持,但我在将所有任务放在一起时遇到了问题,主要是因为结构。

... 终于在写这篇文章的过程中找到了一些解决方案,看起来好像我回答了我自己的问题,但我仍然想知道更熟练的人的答案,因为我找到的解决方案有点复杂,我认为。详细信息和两个子问题在上下文中稍后出现。

很抱歉由于我的知识有限而造成的长度和可能的错误信息。

简单的例子。这是我结束的最后一个工作。一开始没有 SomeStruct.ToString()/Parse() 方法,也没有 JsConfig 设置。

using Newtonsoft.Json;
using ServiceStack;
using ServiceStack.DataAnnotations;
using ServiceStack.Ormlite;
using ServiceStack.Text;
using System.Diagnostics;

namespace Test
{
    public class MainObject
    {
        public int Id { get; set; }
        public string StringProp { get; set; }
        public SomeObject ObjectProp { get; set; }
        public SomeStruct StructProp { get; set; }
    }

    public class SomeObject
    {
        public string StringProp { get; set; }
    }

    public struct SomeStruct
    {
        public string StringProp { get; set; }

        public override string ToString()
        {
            // Unable to use .ToJson() here (ServiceStack does not serialize structs).
            // Unable to use ServiceStack's JSON.stringify here because it just takes ToString() => stack overflow.
            // => Therefore Newtonsoft.Json used.
            var serializedStruct = JsonConvert.SerializeObject(this);
            return serializedStruct;
        }

        public static SomeStruct Parse(string json)
        {
            // This method behaves differently for just deserialization or when part of Save().
            // Details in the text.
            // After playing with different options of altering the json input I ended with just taking what comes.
            // After all it is not necessary,but maybe useful in other situations.
            var structItem = JsonConvert.DeserializeObject<SomeStruct>(json);
            return structItem;
        }
    }

    internal class ServiceStackMariaDbStructTest
    {
        private readonly MainObject _mainObject = new MainObject
        {
            ObjectProp = new SomeObject { StringProp = "SomeObject's String" },StringProp = "MainObject's String",StructProp = new SomeStruct { StringProp = "SomeStruct's String" }
        };

        public ServiceStackMariaDbStructtest()
        {
            // This one line is needed to store complex types as blobbed JSON in MariaDB.
            MysqLDialect.Provider.StringSerializer = new JsonStringSerializer();

            JsConfig<SomeStruct>.RawSerializefn = someStruct => JsonConvert.SerializeObject(someStruct);
            JsConfig<SomeStruct>.RawDeserializefn = json => JsonConvert.DeserializeObject<SomeStruct>(json);
        }

        public void Test_Serialization()
        {
            try
            {
                var json = _mainObject.ToJson();
                if (!string.IsNullOrEmpty(json))
                {
                    var objBack = json.FromJson<MainObject>();
                }
            }
            catch (System.Exception ex)
            {
                Debug.WriteLine(ex.Message);
            }
        }

        public void Test_Save()
        {
            var cs = "ConnectionStringToMariaDB";
            var dbf = new OrmliteConnectionFactory(cs,MysqLDialect.Provider);
            using var db = dbf.OpenDbConnection();
            db.DropAndCreateTable<MainObject>();

            try
            {
                db.Save(_mainObject);
                var dbObject = db.SingleById<MainObject>(_mainObject.Id);
            }
            catch (System.Exception ex)
            {
                Debug.WriteLine(ex.Message);
            }
        }
    }
}

什么(我认为)我知道/已经尝试过但起初并没有帮助自己解决它:

  • ServiceStack 在 DB 中认将复杂类型存储为 blobed JSV(第一部分的最后一段:https://github.com/ServiceStack/ServiceStack.OrmLite),因此有必要按照建议的方式设置它:MysqLDialect.Provider.StringSerializer = new JsonStringSerializer(); ({{3 }})
    => 认 JSV 更改为 JSON。
  • ServiceStack 的序列化不适用于结构体,必须以特殊方式对待它们:
    1. a) 根据 https://github.com/ServiceStack/ServiceStack.OrmLite#pluggable-complex-type-serializers 和示例 https://github.com/ServiceStack/ServiceStack.Text#c-structs-and-value-types,有必要实现 TStruct.ToString()static TStruct.ParseJson()/ParseJsv() 方法

      b) 根据 https://github.com/ServiceStack/ServiceStack.Text/#using-structs-to-customize-json 和单元测试 https://github.com/ServiceStack/ServiceStack.Text/#typeserializer-details-jsv-format,它应该是 TStruct.ToString()(与 a 中相同)和 static TStruct.Parse()

      子问题 #1:哪一个是正确的?对我来说,ParseJson() 从来没有被调用过,Parse() 是。文档问题还是用于其他情况?

      我实施了选项 b)。结果:

      • IDbConnection.Save(_mainObject) 将项目保存到 MariaDB。成功。
        通过保存过程 ToString()Parse()调用。在 Parse 中,传入的 JSON 看起来是这样的:
        "{\"StringProp\":\"SomeStruct's String\"}"。很好。
      • 序列化有效。成功。
      • 反序列化失败。我不知道原因,但传入 Parse() 的 JSON 被“双重转义”:
        "{\\\"StringProp\\\":\\\"SomeStruct's String\\\"}"

      子问题 #2:为什么 Parse 中的反序列化“双重转义”?

    2. 我尝试使用 JsConfig(和 Newtonsoft.Json 来获得正确的 JSON)来解决结构:

      JsConfig<SomeStruct>.Serializefn = someStruct => JsonConvert.SerializeObject(someStruct);
      JsConfig<SomeStruct>.DeSerializefn = json => JsonConvert.DeserializeObject<SomeStruct>(json);
      

      a) 起初没有在 TStruct 中定义 ToString() 和 Parse()。 结果:

      • 保存失败:保存期间使用的 json 中的 JsonConvert.DeserializeObject(json) 输入只是类型名称 "WinAmbPrototype.someStruct"
      • 反/序列化成功。

      b) 然后我也使用 Newtonsoft.Json 实现了 ToString()。 在保存期间使用 ToString() 而不是 JsConfig.Serializefn,即使 JsConfig.Serializefn 仍然设置(也许根据设计,我不判断)。结果:

      • 保存失败:但是在保存期间调用的 Deserializefn 的 json 输入改变了,现在它是 JSV-like "{StringProp:SomeStruct's String}",但仍然不能反序列化为 JSON。
      • 反/序列化成功。
    3. 然后(在写这篇文章时我仍然没有任何解决方案)我发现 JsConfig.Raw* "overrides" 并尝试了它们:

      JsConfig<SomeStruct>.RawSerializefn = someStruct => JsonConvert.SerializeObject(someStruct);
      JsConfig<SomeStruct>.RawDeserializefn = json => JsonConvert.DeserializeObject<SomeStruct>(json);
      

      a) 最初没有在 TStruct 中定义 ToString() 和 Parse()。 结果与 2a 中的相同。

      b) 然后我实现了 ToString()。 结果:

      • 两者都有效。此任务不需要 Parse() 方法

      但这是非常脆弱的设置:

      • 如果我删除ToString(),它会失败(现在我明白为什么了,认的 ToString 生成的 JSON 只在 2a、3a 中输入了名称)。
      • 如果我删除RawSerializefn 设置,它会在 RawDeserializefn(“双重转义”JSON)中失败。

有没有更简单的解决方案?如果有人为我指明更好的方向,我会很高兴。

可以接受的可能是两个(由于情况不同,两者都可以访问):

  • 如果我是 TStruct 所有者:仅使用纯 TStruct.ToString()static TStruct.Parse()支持 ServiceStack 开箱即用的反序列化和 DB(在 Parse() 中没有不同的输入)。
  • 如果我是 TStruct 的使用者,没有实现 JSON 支持并且我无法访问它的代码:直到现在我没有找到方法,如果 ToString 没有实现:保存到 DB 不起作用。也许可以确保 JsConfig 序列化函数足以用于反/序列化以及在保存到数据库期间使用时。

最好的方法是不使用其他依赖项(例如 Newtonsoft.Json)来序列化结构。也许一些JsConfig.ShallProcessstructs = true;警告:只是一个提示,从 2021 年 4 月 2 日起不起作用在这种情况下会很好。

解决方法

ServiceStack 将结构视为单一标量值类型,就像大多数核心 BCL 值类型(例如 TimeSpanDateTime 等)一样。重载 Parse()ToString() 方法以及 Struct 的 Constructor 可让您控制自定义结构的序列化/反序列化。

文档已更正。结构使用 Parse 而类使用 ParseJson/ParseJsv

如果您想序列化模型属性,我建议您改用 class,因为您正在寻找的行为是 POCO DTO 的行为。

如果您想在 RDBMS 中将结构序列化为 DTO,您可以尝试的替代方法是仅使用 JSON.NET 进行复杂类型序列化,例如:

public class JsonNetStringSerializer : IStringSerializer
{
    public To DeserializeFromString<To>(string serializedText) => 
        JsonConvert.DeserializeObject<To>(serializedText);

    public object DeserializeFromString(string serializedText,Type type) =>
        JsonConvert.DeserializeObject(serializedText,type);

    public string SerializeToString<TFrom>(TFrom from) => 
        JsonConvert.SerializeObject(from);
}

MySqlDialect.Provider.StringSerializer = new JsonNetStringSerializer();

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?