使用SOLID原则序列化自定义对象
我想序列化包含纯数据的模型对象(来自WPF MVVM)。这听起来很简单,但我不想使用.NET Framework中提供的Serialization属性和东西。我只是想用我自己的方式序列化它。
所以这里是我的一个类的简化版本。使用SOLID原则序列化自定义对象
public class EntryKeyValuePair
{
public EntryKeyValuePair(string key, string value, bool isMultiline = false, bool isMandatory = true, bool isProtected = false)
{
Key = key;
Value = value;
IsMultiline = isMultiline;
IsMandatory = isMandatory;
IsProtected = isProtected;
}
public string Key { get; set; }
public string Value { get; set; }
public bool IsMultiline { get; set; }
public bool IsMandatory { get; set; }
public bool IsProtected { get; set; }
public static EntryKeyValuePair FromXML(XElement element, ICipher cipher)
{
string key = cipher.Decrypt(element.Element(nameof(Key)).Value);
string value = cipher.Decrypt(element.Element(nameof(Value)).Value);
bool isMultiline = bool.Parse(element.Element(nameof(IsMultiline)).Value);
bool isMandatory = bool.Parse(element.Element(nameof(IsMandatory)).Value);
bool isProtected = bool.Parse(element.Element(nameof(IsProtected)).Value);
return new EntryKeyValuePair(key, value, isMultiline, isMandatory, isProtected);
}
public XElement ToXML(ICipher cipher)
{
return new XElement(nameof(EntryKeyValuePair),
new XElement(nameof(Key),cipher.Encrypt(Key)),
new XElement(nameof(Value), cipher.Encrypt(Value)),
new XElement(nameof(IsMultiline), IsMultiline), new XElement(nameof(IsMandatory), IsMandatory),
new XElement(nameof(IsProtected), IsProtected));
}
}
这工作得很好。但这违反了单一责任原则,也可能违反其他原则。这也难以保持和扩展。
所以我想找到另一种方式。这里是:
首先我定义了一个IStringFormatter
接口,它可以将数据格式化为任何字符串数据格式,如XML和JSON。 (不知道寿)
interface IStringFormatter
{
string Name { get; set; }
Dictionary<string, string> FieldDictionary { get; }
string Format();
}
这里的XMLStringFormatter的样子:
class XmlStringFormatter : IStringFormatter
{
public XmlStringFormatter()
{
FieldDictionary = new Dictionary<string, string>();
}
public string Name { get; set; }
public Dictionary<string, string> FieldDictionary { get; }
public string Format()
{
var xElement = new XElement(Name, FieldDictionary.Keys.Select(key => new XElement(key, FieldDictionary[key])));
return xElement.ToString();
}
}
然后,我定义的ISerializer
序列化(或者说保存)我的数据对象的IStringFormatter
。
这里是我如何实施这个 “序列化” EntryKeyValurPair
:
internal class EntryKeyValurPairSerializer : ISerializer<EntryKeyValuePair>
{
public EntryKeyValuePair DeSerialize(IStringFormatter stringFormatter)
{
Dictionary<string, string> fieldDictionary = stringFormatter.FieldDictionary;
try {
string key = fieldDictionary[nameof(EntryKeyValuePair.Key)];
string value = fieldDictionary[nameof(EntryKeyValuePair.Value)];
bool isMandatory = bool.Parse(fieldDictionary[nameof(EntryKeyValuePair.IsMandatory)]);
bool isProtected = bool.Parse(fieldDictionary[nameof(EntryKeyValuePair.IsProtected)]);
bool isMultiline = bool.Parse(fieldDictionary[nameof(EntryKeyValuePair.IsMultiline)]);
return new EntryKeyValuePair(key, value, isMultiline, isMandatory, isProtected);
}
catch (KeyNotFoundException ex) {
throw new SerializationException(ex);
}
catch (FormatException ex) {
throw new SerializationException(ex);
}
}
public void Serialize(EntryKeyValuePair obj, IStringFormatter stringFormatter)
{
stringFormatter.Name = nameof(EntryKeyValuePair);
Dictionary<string, string> fieldDictionary = stringFormatter.FieldDictionary;
fieldDictionary.Add(nameof(EntryKeyValuePair.Key), obj.Key);
fieldDictionary.Add(nameof(EntryKeyValuePair.Value), obj.Value);
fieldDictionary.Add(nameof(EntryKeyValuePair.IsMandatory), obj.IsMandatory.ToString());
fieldDictionary.Add(nameof(EntryKeyValuePair.IsProtected), obj.IsProtected.ToString());
fieldDictionary.Add(nameof(EntryKeyValuePair.IsMultiline), obj.IsMultiline.ToString());
}
}
现在能正常工作。但问题是当我在我的数据类中有一个像List<Entry>
(其中Entry是另一个数据类)的复杂类型时。
由于IStringFormatter
包含Dictionary<string, string>
,我不能只将List<Entry>
转换为字符串,因为我不知道它在ISerializer
的上下文中想要什么样的IStringFormatter
。我怎样才能解决这个问题?我也想知道我的解决方案是否遵守SOLID原则。如果您可以提出更好的解决方案(不是典型的.NET序列化),我将不胜感激。
编写自己的序列化程序可能是一个有趣的任务,但我怀疑这是一个好主意。
据我所知,你想保持你的模型干净,没有任何序列化的特定属性。我认为“典型的.NET序列化”是指.Net框架中包含的方法。
为了简单起见,我们将使用这些简单的类为例:
class Customer
{
public string Name { get; set; }
public int Age { get; set; }
public List<Order> Orders { get; set; }
}
class Order
{
public int Id { get; set; }
public string Details { get; set; }
}
一个容易的选择是使用Json.NET:
var customer = new Customer
{
Name = "Darth Vader",
Age = 45,
Orders = new List<Order>
{
new Order { Id = 1, Details = "Order1" },
new Order { Id = 2, Details = "Order2" }
}
};
string json = JsonConvert.SerializeObject(customer);
所以你可以看到你不需要添加任何自定义属性到Customer
类。它会工作,直到你想序列化所有的公共属性。
产生的JSON将是:
{
"Name": "Darth Vader",
"Age": 45,
"Orders": [
{
"Id": 1,
"Details": "Order1"
},
{
"Id": 2,
"Details": "Order2"
}
]
}
之后,你可以随时反序列化:
var customer = JsonConvert.DeserializeObject<Customer>(json);
比方说,你不希望被列入Age
财产。在这种情况下,我会建议创建将只用于序列化不同的类:
class CostomerSerializationContract
{
public string Name { get; set; }
public List<Order> Orders { get; set; }
}
如果这种方法是,你必须系列化单独的类,你可以添加任何自定义这主要优势属性那里,如果您选择使用其他序列化程序,而不违反SOLID原则。主要缺点是您需要手动同步两个对象。
从源类创建序列化合约时,可以使用AutoMapper减少手动工作。
这也适用于列表以及?它也有公开获得,设置属性 –
是的,它会的。我在我的答案中添加了一个例子。 –
如果我有一个'BitmapSource'(不能序列化)属性呢? 。我可以将它转换为base64,但我怎么能告诉序列化器做到这一点? –