[英]How to use ServiceStack to store POCOs to MariaDB having complex types (objects and structs) blobbed as JSON?
I've got following setup: C#, ServiceStack, MariaDB, POCOs with objects and structs, JSON.我有以下设置:C#、ServiceStack、MariaDB、带有对象和结构的 POCO、JSON。
The main question is : how to use ServiceStack to store POCOs to MariaDB having complex types (objects and structs) blobbed as JSON and still have working de/serialization of the same POCOs?主要问题是:如何使用 ServiceStack 将 POCO 存储到 MariaDB 具有复杂类型(对象和结构)的 JSON 并且仍然可以对相同的 POCO 进行反序列化/序列化? All of these single tasks are supported, but I had problems when all put together mainly because of structs.
所有这些单个任务都受支持,但是当所有这些任务放在一起时我遇到了问题,主要是因为结构。
... finally during writing this I found some solution and it may look like I answered my own question, but I still would like to know the answer from more skilled people, because the solution I found is a little bit complicated, I think. ...最后在写这篇文章的过程中,我找到了一些解决方案,看起来我回答了我自己的问题,但我仍然想知道更多技术人员的答案,因为我认为我找到的解决方案有点复杂。 Details and two subquestions arise later in the context.
细节和两个子问题在上下文中稍后出现。
Sorry for the length and for possible misinformation caused by my limited knowledge.对不起,由于我的知识有限,可能造成的长度和错误信息。
Simple example.简单的例子。 This is the final working one I ended with.
这是我结束的最后一个工作。 At the beginning there were no
SomeStruct.ToString()/Parse()
methods and no JsConfig
settings.一开始没有
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);
}
}
}
}
What (I think) I know / have tried but at first didn't help to solve it myself:我知道/尝试过什么(我认为),但起初并没有帮助自己解决:
MySqlDialect.Provider.StringSerializer = new JsonStringSerializer();
MySqlDialect.Provider.StringSerializer = new JsonStringSerializer();
( https://github.com/ServiceStack/ServiceStack.OrmLite#pluggable-complex-type-serializers ) a) according to https://github.com/ServiceStack/ServiceStack.Text#c-structs-and-value-types and example https://github.com/ServiceStack/ServiceStack.Text/#using-structs-to-customize-json it is necessary to implement TStruct.ToString()
and static TStruct.ParseJson()/ParseJsv()
methods. a) 根据https://github.com/ServiceStack/ServiceStack.Text#c-structs-and-value-types和示例https://github.com/ServiceStack/ServiceStack.Text/#using-structs-to- custom -json需要实现
TStruct.ToString()
和static TStruct.ParseJson()/ParseJsv()
方法。
b) according to https://github.com/ServiceStack/ServiceStack.Text/#typeserializer-details-jsv-format and unit tests https://github.com/ServiceStack/ServiceStack.Text/blob/master/tests/ServiceStack.Text.Tests/CustomStructTests.cs it shall be TStruct.ToString()
(the same as in a) and static TStruct.Parse()
. b) 根据https://github.com/ServiceStack/ServiceStack.Text/#typeserializer-details-jsv-format和单元测试https://github.com/ServiceStack/ServiceStack.Text/blob/master/tests/ServiceStack .Text.Tests/CustomStructTests.cs应为
TStruct.ToString()
(与 a 中相同)和static TStruct.Parse()
。
Subquestion #1 : which one is the right one?子问题#1 :哪一个是正确的? For me,
ParseJson()
was never called, Parse()
was.对我来说,
ParseJson()
从未被调用过, Parse()
被调用过。 Documentation issue or is it used in other situation?文档问题还是在其他情况下使用?
I implemented option b).我实施了选项 b)。 Results:
结果:
IDbConnection.Save(_mainObject)
saved the item to MariaDB. IDbConnection.Save(_mainObject)
将项目保存到 MariaDB。 Success.ToString()
and Parse()
were called.ToString()
和Parse()
。 In Parse, incoming JSON looked this way:"{\"StringProp\":\"SomeStruct's String\"}"
. "{\"StringProp\":\"SomeStruct's String\"}"
。 Fine."{\\\"StringProp\\\":\\\"SomeStruct's String\\\"}"
Subquestion #2 : Why the "double-escaping" in Parse on deserialization?子问题 #2 :为什么 Parse 中的“双重转义”反序列化?
I tried to solve structs with JsConfig (and Newtonsoft.Json to get proper JSON):我尝试使用 JsConfig 解决结构(和 Newtonsoft.Json 以获得正确的 JSON):
JsConfig<SomeStruct>.SerializeFn = someStruct => JsonConvert.SerializeObject(someStruct); JsConfig<SomeStruct>.DeSerializeFn = json => JsonConvert.DeserializeObject<SomeStruct>(json);
a) at first without ToString() and Parse() defined in the TStruct. a)一开始没有在 TStruct 中定义 ToString() 和 Parse()。 Results:
结果:
json
input in JsonConvert.DeserializeObject(json)
that is used during Save was just type name "WinAmbPrototype.SomeStruct"
.JsonConvert.DeserializeObject(json)
中的json
输入只是类型名称"WinAmbPrototype.SomeStruct"
。 b) then I implemented ToString() also using Newtonsoft.Json. b)然后我也使用 Newtonsoft.Json 实现了 ToString()。 During Save
ToString()
was used instead of JsConfig.SerializeFn
even the JsConfig.SerializeFn was still set (maybe by design, I do not judge).在 Save
ToString()
被用来代替JsConfig.SerializeFn
甚至 JsConfig.SerializeFn 仍然设置(也许是设计使然,我不判断)。 Results:结果:
json
input of DeserializeFn called during Save changed, now it was JSV-like "{StringProp:SomeStruct's String}"
, but still not deserializable as JSON.json
输入发生了变化,现在它类似于 JSV "{StringProp:SomeStruct's String}"
,但仍不能反序列化为 JSON。 Then (during writing this I was still without any solution) I found JsConfig.Raw* "overrides" and tried them:然后(在写这篇文章时我仍然没有任何解决方案)我找到了 JsConfig.Raw* “覆盖”并尝试了它们:
JsConfig<SomeStruct>.RawSerializeFn = someStruct => JsonConvert.SerializeObject(someStruct); JsConfig<SomeStruct>.RawDeserializeFn = json => JsonConvert.DeserializeObject<SomeStruct>(json);
a) at first without ToString() and Parse() defined in the TStruct. a)一开始没有在 TStruct 中定义 ToString() 和 Parse()。 Results are the same as in 2a.
结果与 2a 相同。
b) then I implemented ToString(). b)然后我实现了 ToString()。 Results:
结果:
Parse()
method needed for this task.Parse()
方法。 But it is very fragile setup:但这是非常脆弱的设置:
ToString()
, it failed (now I understand why, default ToString produced JSON with just type name in 2a, 3a).ToString()
,它就会失败(现在我明白了为什么,默认 ToString 生成 JSON ,只在 2a、3a 中输入名称)。RawSerializeFn
setting, it failed in RawDeserializeFn
("double-escaped" JSON).RawSerializeFn
设置,它会在RawDeserializeFn
(“双转义”JSON)中失败。 Is there some simpler solution?有没有更简单的解决方案? I would be very glad if someone points me to better direction.
如果有人指出我更好的方向,我会很高兴。
Acceptable would be maybe two (both of them accessible because of different circumstances):可接受的可能是两个(由于不同的情况,它们都可以访问):
TStruct.ToString()
and static TStruct.Parse()
to support out of the box de/serialization and DB by ServiceStack (without different input in Parse()
).TStruct.ToString()
和static TStruct.Parse()
来支持 ServiceStack 的开箱即用反序列化和 DB(在Parse()
中没有不同的输入)。 And the best one would be without employing other dependency (eg Newtonsoft.Json) to serialize structs.最好的方法是不使用其他依赖项(例如 Newtonsoft.Json)来序列化结构。 Maybe some
JsConfig.ShallProcessStructs = true;
也许一些
JsConfig.ShallProcessStructs = true;
( WARNING: just a tip, not working as of 2021-04-02 ) would be fine for such situations. (警告:只是一个提示,自 2021-04-02 起不起作用)对于这种情况是可以的。
ServiceStack treats structs like a single scalar value type, just like most of the core BCL Value Types (eg TimeSpan
, DateTime
, etc). ServiceStack 将结构视为单个标量值类型,就像大多数核心 BCL 值类型(例如
TimeSpan
、 DateTime
等)一样。 Overloading the Parse()
and ToString()
methods and Struct's Constructor
let you control the serialization/deserialization of custom structs.重载
Parse()
和ToString()
方法以及 Struct 的Constructor
可以让您控制自定义结构的序列化/反序列化。
Docs have been corrected.
文档已更正。 Structs use
Parse
whilst classes useParseJson/ParseJsv
结构使用
Parse
,而类使用ParseJson/ParseJsv
If you want to serialize a models properties I'd suggest you use a class
instead as the behavior you're looking for is that of a POCO DTO.如果您想序列化模型属性,我建议您使用
class
代替,因为您正在寻找的行为是 POCO DTO 的行为。
If you want to have structs serailized as DTOs in your RDBMS an alternative you can try is to just use JSON.NET for the complex type serialization, eg:如果您想在 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();
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.