问题描述
C:\Users\YOURUSERNAME \ .gradle\caches
我想按以下顺序进行序列化
public class Vehicle{
public int VehicleId {get;set;};
public string Make {get;set;};
public string Model{get;set;}
}
在Vehicle类中,我有大约100个这样的属性,对于每个车辆属性,我想附加一个元数据Applieto,这将对下游系统有所帮助。 AppliesTo属性为静态,其值在设计时定义。现在如何将AppliesTo元数据附加到每个属性,然后依次序列化为XML?
解决方法
您可以使用System.Xml.Linq
中的XElement
来实现此目的。由于您的数据是静态的,因此可以轻松分配它们。下面的示例代码-
XElement data= new XElement("Vehicle",new XElement("VehicleId",new XAttribute("AppliesTo","C1"),"1244"),new XElement("Make","Common"),"HXV"),new XElement("Model","C2"),"34 - 34")
);
//OUTPUT
<Vehicle>
<VehicleId AppliesTo="C1">1244</VehicleId>
<Make AppliesTo="Common">HXV</Make>
<Model AppliesTo="C2">34 - 34</Model>
</Vehicle>
如果您对System.Xml.Linq
不感兴趣,则可以选择XmlSerializer
类。为此,您需要为vehicle
的每个属性定义单独的类。以下是示例代码,您可以将其扩展为Make and Model
-
[XmlRoot(ElementName = "VehicleId")]
public class VehicleId
{
[XmlAttribute(AttributeName = "AppliesTo")]
public string AppliesTo { get; set; }
[XmlText]
public string Text { get; set; }
}
[XmlRoot(ElementName = "Vehicle")]
public class Vehicle
{
[XmlElement(ElementName = "VehicleId")]
public VehicleId VehicleId { get; set; }
//Add other properties here
}
然后创建测试数据并使用XmlSerializer
类构造XML-
Vehicle vehicle = new Vehicle
{
VehicleId = new VehicleId
{
Text = "1244",AppliesTo = "C1",}
};
XmlSerializer testData = new XmlSerializer(typeof(Vehicle));
var xml = "";
using (var sww = new StringWriter())
{
using (XmlWriter writer = XmlWriter.Create(sww))
{
testData.Serialize(writer,vehicle);
xml = sww.ToString(); // XML
}
}
,
以所需的方式使用默认的.NET XML序列化器(System.Xml.Serialization.XmlSerializer)并非易事或理想,但这是可能的。此答案显示了如何创建一个类结构来保存您的主数据和元数据,然后使用XmlAttributeAttribute标记属性,以便将其序列化为XML属性。
假设:
关于您要实现的实现有很多未知数,例如:
- 您要使用的XML序列化程序(.NET的默认值?)
- 注入“ AppliesTo”的机制(属性?)
- 您关心反序列化吗?
此答案假定使用默认的.NET序列化程序,反序列化很重要,并且您不关心注入元数据的确切方法。
关键概念:
- 同时包含我们的主要属性值和元数据的通用类(请参见
PropertyWithAppliesTo<T>
) - 在通用类的元数据上使用
XmlAttributeAttribute
,因此将其作为XML属性写入父级属性 - 在通用类的主数据上使用
XmlTextAttribute
,因此将其写为父属性的Xml文本(而不是子属性) - 对于要序列化的每个值,包括要序列化的主要类型(在这种情况下为
Vehicle
)上的两个属性:一种是使用元数据进行序列化的新通用类型,另一种是使用{ {1}}提供对属性值的“预期”访问 - 使用
XmlIgnoreAttribute
更改序列化属性的名称(使其与预期名称匹配)
代码:
XmlElementAttribute
运行时,字符串using System;
using System.IO;
using System.Xml.Serialization;
namespace SomeNamespace
{
public class Program
{
static void Main()
{
var serializer = new XmlSerializer(typeof(Vehicle));
string s;
var vehicle = new Vehicle { VehicleId = 1244 };
//serialize
using (var writer = new StringWriter())
{
serializer.Serialize(writer,vehicle);
s = writer.ToString();
Console.WriteLine(s);
}
// edit the serialized string to test deserialization
s = s.Replace("Common","C1");
//deserialize
using (var reader = new StringReader(s))
{
vehicle = (Vehicle)serializer.Deserialize(reader);
Console.WriteLine($"AppliesTo attribute for VehicleId: {vehicle.VehicleIdMeta.AppliesTo}");
}
}
}
public class Vehicle
{
[XmlElement(ElementName = "VehicleId")] // renames to remove the 'Meta' string
public PropertyWithAppliesTo<int> VehicleIdMeta { get; set; } = new PropertyWithAppliesTo<int>("Common");
[XmlIgnore] // this value isn't serialized,but the property here for easy syntax
public int VehicleId
{
get { return VehicleIdMeta.Value; }
set { VehicleIdMeta.Value = value; }
}
}
public class PropertyWithAppliesTo<T>
{
[XmlAttribute] // tells serializer this should be an attribute on this element,not a property
public string AppliesTo { get; set; } = string.Empty;
[XmlText] // tells serializer to not write this as a property,but as the main XML text
public T Value { get; set; } = default;
public PropertyWithAppliesTo() : this(string.Empty) { }
public PropertyWithAppliesTo(string appliesTo) : this(appliesTo,default) { }
public PropertyWithAppliesTo(string appliesTo,T initialValue)
{
AppliesTo = appliesTo;
Value = initialValue;
}
}
}
如下所示:
s
其他说明:
- 您可以看到如何向
<?xml version=\"1.0\" encoding=\"utf-16\"?> <Vehicle xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"> <VehicleId AppliesTo="Common">1244</VehicleId> </Vehicle>
添加更多属性:添加标记为Vehicle
的类型PropertyWithAppliesTo<T>
的属性以为其指定所需的名称,然后添加类型T的属性标记为XmlElement
并环绕所需的XmlIgnore
的 - 您可以通过更改
Value
的构造函数的输入并为其提供不同的元数据字符串来控制AppliesTo
的值。 - 如果您不希望库的使用者在IntelliSense中看到“元”属性,则可以使用EditorBrowsableAttribute。使用源和项目引用时,它不会对您隐藏任何东西;仅在引用编译的dll时隐藏。
诚然,这是向类添加属性的一种烦人的方法。但是,如果要使用默认的.NET XML序列化程序,则这是一种实现所需XML的方法。