具有附加元数据的类属性的XML序列化

问题描述

我有一个如下实体

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序列化程序,反序列化很重要,并且您不关心注入元数据的确切方法。

关键概念:

  1. 同时包含我们的主要属性值和元数据的通用类(请参见PropertyWithAppliesTo<T>
  2. 在通用类的元数据上使用XmlAttributeAttribute,因此将其作为XML属性写入父级属性
  3. 在通用类的主数据上使用XmlTextAttribute,因此将其写为父属性的Xml文本(而不是子属性)
  4. 对于要序列化的每个值,包括要序列化的主要类型(在这种情况下为Vehicle)上的两个属性:一种是使用元数据进行序列化的新通用类型,另一种是使用{ {1}}提供对属性值的“预期”访问
  5. 使用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的方法。