XML class 属性的序列化和额外的元数据
XML serialisation for class properties with additional meta data
我有一个实体如下
public class Vehicle{
public int VehicleId {get;set;};
public string Make {get;set;};
public string Model{get;set;}
}
我想连载如下
<Vehicle>
<VehicleId AppliesTo="C1">1244</VehicleId>
<Make AppliesTo="Common" >HXV</Make>
<Model AppliesTo="C2">34-34</Model>
</Vehicle>
我在 Vehicle class 中有大约 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", new XAttribute("AppliesTo", "Common"), "HXV"),
new XElement("Model", new XAttribute("AppliesTo", "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
class。为此,您需要为 vehicle
的每个 属性 定义单独的 classes。下面是示例代码,您可以将其扩展为 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
class构造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) in the way you want, but it's possible. This answer shows how to create a class structure to hold both your main data and the metadata, then use XmlAttributeAttribute 来标记 属性 因此它被序列化为 XML 属性并不容易或不理想。
假设:
关于您的预期实施有许多未知数,例如:
- 您要使用的 XML 序列化程序(.NET 的默认设置?)
- 注入机制'AppliesTo'(属性?)
- 你关心反序列化吗?
此答案假定默认的 .NET 序列化程序,反序列化很重要,并且您不关心注入元数据的确切方法。
关键概念:
- 通用 class 来保存我们的主要 属性 值和元数据(参见
PropertyWithAppliesTo<T>
)
- 在通用 class' 元数据上使用
XmlAttributeAttribute
,因此它被写为父 属性 上的 XML 属性
- 在通用 class' 主数据上使用
XmlTextAttribute
,因此它被写为父 属性 的 Xml 文本(而不是 sub-property)
- 在要序列化的每个值的要序列化的主要类型(在本例中为
Vehicle
)上包括两个属性:一种是使用元数据序列化的新通用类型,另一种是标记为原始类型XmlIgnoreAttribute
提供 'expected' 访问 属性 的值
- 使用
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>
类型的 属性 标记为 XmlElement
并为其指定您想要的名称,然后然后是标有 XmlIgnore
的 T 类型的 属性,它环绕着您想要的 Value
。
- 您可以通过更改
PropertyWithAppliesTo<T>
的构造函数的输入并为其提供不同的元数据字符串来控制 AppliesTo
的值。
- 如果您不希望库的使用者在 IntelliSense 中看到 'meta' 属性,您可以使用 EditorBrowsableAttribute。在使用源代码和项目引用时,它不会向您隐瞒任何事情;它仅在引用已编译的 dll 时隐藏。
无可否认,这是一种向 class 添加属性的恼人方式。但是如果你想使用默认的 .NET XML 序列化程序,这是一种实现你想要的 XML 的方法。
我有一个实体如下
public class Vehicle{
public int VehicleId {get;set;};
public string Make {get;set;};
public string Model{get;set;}
}
我想连载如下
<Vehicle>
<VehicleId AppliesTo="C1">1244</VehicleId>
<Make AppliesTo="Common" >HXV</Make>
<Model AppliesTo="C2">34-34</Model>
</Vehicle>
我在 Vehicle class 中有大约 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", new XAttribute("AppliesTo", "Common"), "HXV"),
new XElement("Model", new XAttribute("AppliesTo", "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
class。为此,您需要为 vehicle
的每个 属性 定义单独的 classes。下面是示例代码,您可以将其扩展为 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
class构造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) in the way you want, but it's possible. This answer shows how to create a class structure to hold both your main data and the metadata, then use XmlAttributeAttribute 来标记 属性 因此它被序列化为 XML 属性并不容易或不理想。
假设:
关于您的预期实施有许多未知数,例如:
- 您要使用的 XML 序列化程序(.NET 的默认设置?)
- 注入机制'AppliesTo'(属性?)
- 你关心反序列化吗?
此答案假定默认的 .NET 序列化程序,反序列化很重要,并且您不关心注入元数据的确切方法。
关键概念:
- 通用 class 来保存我们的主要 属性 值和元数据(参见
PropertyWithAppliesTo<T>
) - 在通用 class' 元数据上使用
XmlAttributeAttribute
,因此它被写为父 属性 上的 XML 属性
- 在通用 class' 主数据上使用
XmlTextAttribute
,因此它被写为父 属性 的 Xml 文本(而不是 sub-property) - 在要序列化的每个值的要序列化的主要类型(在本例中为
Vehicle
)上包括两个属性:一种是使用元数据序列化的新通用类型,另一种是标记为原始类型XmlIgnoreAttribute
提供 'expected' 访问 属性 的值 - 使用
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>
类型的 属性 标记为XmlElement
并为其指定您想要的名称,然后然后是标有XmlIgnore
的 T 类型的 属性,它环绕着您想要的Value
。 - 您可以通过更改
PropertyWithAppliesTo<T>
的构造函数的输入并为其提供不同的元数据字符串来控制AppliesTo
的值。 - 如果您不希望库的使用者在 IntelliSense 中看到 'meta' 属性,您可以使用 EditorBrowsableAttribute。在使用源代码和项目引用时,它不会向您隐瞒任何事情;它仅在引用已编译的 dll 时隐藏。
无可否认,这是一种向 class 添加属性的恼人方式。但是如果你想使用默认的 .NET XML 序列化程序,这是一种实现你想要的 XML 的方法。