我已经获得了一个项目,我将在其中设计一个普遍的"存储商店值并根据需要修改它们的方法(在运行程序之外)。这些值用于测试目的(即最大和最小电压和电流阈值)。理想情况下,我应该能够在多个程序和项目中共享这些值。
过去,我们会为此目的定义自己的脚本文件(通常是某种形式的CSV文件),不用说,它是凌乱和低效的。
现在,我给出这个项目时的直接想法是使用对象序列化(特别是XML序列化,因为它是人类可读的)。我们的测试是多种多样的,以至于没有办法提出一个普遍的"格式;所以接下来要做的最好的事情是自动化格式化。它允许我在程序输入时反序列化对象,然后在我完成它时再次序列化它。最重要的是,我可以进入生成的XML文件并手动修改其内容。另外,我可以像常规对象一样使用数据。所以我开始将所有这些值存储在对象中并将它们序列化。
但是,我们的测试参数经常变化;有时我们可能会添加或删除测试用例等等。不幸的是,为了做到这一点,我需要修改我的对象。现在我离开了同一个对象的不同版本,我需要支持所有这些版本(或者至少将数据上转换为最新版本的对象类型)。
当然,一种解决方案是为每个版本使用继承,但这会变得混乱,并且它不是一种自然的编码方式(特别是在您测试硬件时)。反思可能是另一种选择,但我认为它涉及一个手动解析器(我仍然需要支持多个版本)。我很欣赏有关最佳方法的任何意见。谢谢
请注意,我已使用DataContractSerializer
来处理序列化和反序列化。
这是我的代码如何工作的示例类; :
主要功能:
namespace Test
{
internal class Program
{
private static void Main(string[] args)
{
SerializableSingleton<Class1>.initInstance("class1Singleton.xml");
Class1 obj1 = SerializableSingleton<Class1>.Instance;
Class1 obj2 = new SerializableInstance<Class1>("class1Instance.xml").Data;
}
}
}
要序列化的类:
namespace Test
{
[DataContract()]
class Class1 : AbstractSerializeableObject
{
protected override XMLVersion ObjectVersion
{
get { return version; }
}
XMLVersion version = new XMLVersion(1, 0, 0, "Init Version");
[DataMember(Name = "First Number")]
int number = 1;
[DataMember()]
string str = "hello";
[DataMember(Name = "Nested Class")]
NestedClass1 nClass = new NestedClass1();
[DataContract()]
class NestedClass1 :AbstractSerializeableObject
{
protected override XMLVersion ObjectVersion
{
get { return version; }
}
XMLVersion version = new XMLVersion(1, 0, 0, "Init Version");
[DataMember(Name = "Second Number")]
int number = 2;
[DataMember()]
string str = "world";
}
}
}
生成的XML文件:
class1Singleton.xml
<?xml version="1.0" encoding="utf-8"?>
<Class1 xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/Test">
<First_x0020_Number>1</First_x0020_Number>
<Nested_x0020_Class>
<Second_x0020_Number>2</Second_x0020_Number>
<str>world</str>
</Nested_x0020_Class>
<str>hello</str>
</Class1>
class1Instance.xml
<?xml version="1.0" encoding="utf-8"?>
<Class1 xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/Test">
<First_x0020_Number>1</First_x0020_Number>
<Nested_x0020_Class>
<Second_x0020_Number>2</Second_x0020_Number>
<str>world</str>
</Nested_x0020_Class>
<str>hello</str>
</Class1>
请注意,此处的所有内容均应显示;生成的XML文件没有问题(到目前为止)。这些是我期望的结果。单例和实例的内容有很多层,并不是什么重要的事情。重要的是序列化本身。重要的部分是序列化本身和版本控制。
以下是我如何进行序列化和反序列化:
namespace XMLSerializationLib
{
/// <summary>
/// The XmlSerializer is used solely to encapulate the serialization and
/// deserialization of objects. It uses generics so most objects can make use
/// of this library, provided that they heed to the warning(s) below:
///
/// -- The object to be serialized MUST have a default, non-parameterized
/// contructor
///
/// </summary>
/// <typeparam name="T"> The object type to be serialized/deserialized </typeparam>
internal static class XmlSerializer<T>
{
/* ========================================================================= */
/* Functions */
/* ========================================================================= */
/// <summary>
/// Serializes an object of type T into an XML file
/// which can be edited outside of the program.
/// </summary>
/// <param name="obj"> The object which should be serialized </param>
/// <param name="path"> The path of the XML file to be generated </param>
internal static void Serialize(T obj, string path)
{
if (!(obj is AbstractSerializeableObject))
throw new SerializerTypeException("Serialized Object must inherit AbstractSerializeableObject");
// Make XML readable
var settings = new XmlWriterSettings();
settings.Indent = true;
settings.IndentChars = "\t";
#if _NET_4_5_
// Enable serialization of `readonly` properties and types
System.Runtime.Serialization.SerializeReadOnlyTypes = true;
#endif
// Write to XML
using (var writer = XmlWriter.Create(path, settings))
{
DataContractSerializer serializer = new DataContractSerializer(typeof(T));
serializer.WriteObject(writer, obj);
writer.Close();
}
}
/// <summary>
/// Deserializes a provided XML file and stores the retrieved data inside
/// an object of type T.
/// </summary>
/// <param name="path"> The path of the XML file to be loaded </param>
/// <returns>
/// An filled object of type T; if the provided XML cannot be
/// deserialized, then it returns a T object with its default
/// properties.
/// </returns>
internal static T Deserialize(string path)
{
T obj;
DataContractSerializer deserializer = new DataContractSerializer(typeof(T));
XmlReader reader = new XmlTextReader(path);
#if _NET_4_5_
// Enable deserialization of `readonly` properties and types
System.Runtime.Serialization.SerializeReadOnlyTypes = true;
#endif
if (deserializer.IsStartObject(reader))
obj = (T)deserializer.ReadObject(reader);
else
obj = Activator.CreateInstance<T>();
reader.Close();
if (!(obj is AbstractSerializeableObject))
throw new SerializerTypeException("Serialized Object must inherit AbstractSerializeableObject");
return obj;
}
/// <summary>
/// Exception to be thrown if the provided object T does not inherit
/// AbstractSerializeableObject.
/// </summary>
internal class SerializerTypeException : Exception
{
/// <summary>
/// Exception constructor
/// </summary>
/// <param name="message"> Error message </param>
public SerializerTypeException(string message)
: base(message)
{
}
}
}
}
现在问题如下:假设我要使用Class1
对象并将number
字段修改为等于382000
。然后,我将完全删除str
字段,并添加一个名为dbl
的新字段,其值为pi
。结果类看起来像这样:
修改后的Class1
namespace Test
{
[DataContract()]
class Class1 : AbstractSerializeableObject
{
protected override XMLVersion ObjectVersion
{
get { return version; }
}
XMLVersion version = new XMLVersion(1, 0, 0, "Init Version");
[DataMember(Name = "First Number")]
int number = 382000;
[DataMember()]
double dbl = 3.14;
[DataMember(Name = "Nested Class")]
NestedClass1 nClass = new NestedClass1();
[DataContract()]
class NestedClass1 :AbstractSerializeableObject
{
protected override XMLVersion ObjectVersion
{
get { return version; }
}
XMLVersion version = new XMLVersion(1, 0, 0, "Init Version");
[DataMember(Name = "Second Number")]
int number = 2;
[DataMember()]
string str = "world";
}
}
}
现在假设我在一台计算机(计算机1 )上使用原始版本的Class1
运行我的程序的旧版本,并使用新的{{1>更新版本在另一台机器上(机器3 )。为了论证,让我们说我在第三台机器上运行了Class1
的中间版本( Machine2 )。现在我希望能够获取 Machine 1 生成的XML文件并快进,以便兼容数据能够在 Machine 2 和< strong>机器3 ;所有新的数据字段当然都会占用一些默认值。
答案 0 :(得分:0)
在类似的情况下,我使用控制台应用程序将旧的xml更新为新结构。 为此,我写了我对IDataContractSurrogate的认识:
public class SerializerTypeSurrogate : IDataContractSurrogate
{
public virtual Type GetDataContractType(Type type)
{
return type == typeof(Charge) ? typeof(OldCharge) : type;
}
public object GetObjectToSerialize(object obj, Type targetType)
{
return obj;
}
public virtual object GetDeserializedObject(Object obj, Type targetType)
{
var oldClass1= obj as OldClass1;
return oldClass1 != null ? new Class1(oldClass1) : obj;
}
public Type GetReferencedTypeOnImport(string typeName,
string typeNamespace, object customData)
{
return null;
}
public System.CodeDom.CodeTypeDeclaration ProcessImportedType(
System.CodeDom.CodeTypeDeclaration typeDeclaration,
System.CodeDom.CodeCompileUnit compileUnit)
{
return typeDeclaration;
}
public object GetCustomDataToExport(Type clrType, Type dataContractType)
{
return null;
}
public object GetCustomDataToExport(System.Reflection.MemberInfo memberInfo, Type dataContractType)
{
return null;
}
public void GetKnownCustomDataTypes(Collection<Type> customDataTypes)
{
}
}
你应该暂时保留旧的和新的类,新类应该有来自旧类对象的构造函数,但是你可以在以新形式转换所有旧的xmls之后删除它。
要从旧表单转换为新表单,您应该使用下一个代码:
var oldentity = Deserialize<T>(oldXml,
new SerializerTypeSurrogate());
var newXml = FormalizedChangeRecord.Serialize<T>(card);
public static TCard Deserialize<T>(string xmlData, IDataContractSurrogate surrogate) {
var serializer = new DataContractSerializer(typeof (T), new Type[0], 10000, true, true, surrogate);
using (var stringReader = new StringReader(changeRecord.CardData))
{
using (var xmlReader = XmlReader.Create(stringReader, new XmlReaderSettings { CloseInput = false }))
{
var entity= (TCard)serializer.ReadObject(xmlReader);
return entity;
}
}
}
public static string Serialize<T>(T entity)
{
var serializer = new DataContractSerializer(typeof (T));
using (var stringWriter = new StringWriter())
{
using (
var xmlWriter = XmlWriter.Create(stringWriter,
new XmlWriterSettings
{
CloseOutput = false,
Encoding = new UTF8Encoding(false),
OmitXmlDeclaration = true,
Indent = true
}))
{
serializer.WriteObject(xmlWriter, entity);
}
return stringWriter.ToString();
}
}