自定义值类型的 DataContractSerializer
DataContractSerializer for custom Value types
我有一个公开类型 TestTypeOne
的 WCF 服务,该类型当前有一个名为 ProductId
:
的字符串 属性
public class TestTypeOne
{
[DataMember(Name = "ProductId")]
public string ProductId { get; set; } //note it's a string at the moment
}
我想将此 属性 的类型从 string
更改为名为 ProductId
的自定义值类型,但不破坏 WCF 合同(这仅适用于服务器端,客户端应该仍然将 ProductId
视为字符串。)
public class TestTypeOne
{
[DataMember(Name = "ProductId")]
public ProductId ProductId { get; set; }
}
自定义类型是这样的(为简洁起见删除了大部分代码):
public struct ProductId : IEquatable<ProductId>
{
readonly string productId;
public ProductId(string productId)
{
this.productId = productId
}
public override string ToString() => productId ?? string.Empty;
}
使用以下测试代码:
var sb = new StringBuilder();
using (var writer = new XmlTextWriter(new StringWriter(sb)))
{
var dto = new TestTypeOne {
ProductId = new ProductId("1234567890123")
};
var serializer = new DataContractSerializer(typeof(TestTypeOne));
serializer.WriteObject(writer, dto);
writer.Flush();
}
Console.WriteLine(sb.ToString());
序列化时的预期输出应该是:
<Scratch.TestTypeOne xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/Tests">
<ProductId>1234567890123</ProductId>
</Scratch.TestTypeOne>
我已经尝试实施 ISerializable
但这似乎只能让我控制 ProductId
xml 标签的内容,而不是标签本身(所以我可以制作 [= =22=] 发生)。
理想情况下,我希望可以对 ProductId
类型本身做一些事情,因为这种类型在许多地方和许多合同中都有使用。
您是否也尝试过将 DataContract/DataMember 属性添加到此 ProductId class?
即:
[DataContract]
public struct ProductId : IEquatable<ProductId>
{
[DataMember]
readonly string productId;
public ProductId(string productId)
{
this.productId = productId
}
public override string ToString() => productId ?? string.Empty;
}
此外,在这种情况下不需要名称 属性 (Name="ProductId"),因为变量名称与您覆盖它的名称相同。
我认为最简单的方法是实施 IXmlSerializable
:
public struct ProductId : IXmlSerializable
{
readonly string productId;
public ProductId(string productId)
{
this.productId = productId;
}
public override string ToString() => productId ?? string.Empty;
XmlSchema IXmlSerializable.GetSchema() {
return null;
}
void IXmlSerializable.ReadXml(XmlReader reader) {
this = new ProductId(reader.ReadString());
}
void IXmlSerializable.WriteXml(XmlWriter writer) {
writer.WriteString(this.productId);
}
}
要为这种情况调整 WCF xsd 生成(强制它生成 xs:string
)- 您可以使用数据合同代理来生成 xsd。例如你可以有这样的建议:
public class ProductIdSurrogate : IDataContractSurrogate {
public Type GetDataContractType(Type type) {
if (type == typeof(ProductId))
return typeof(string);
return type;
}
public object GetObjectToSerialize(object obj, Type targetType) {
throw new NotImplementedException();
}
public object GetDeserializedObject(object obj, Type targetType) {
throw new NotImplementedException();
}
public object GetCustomDataToExport(MemberInfo memberInfo, Type dataContractType) {
return null;
}
public object GetCustomDataToExport(Type clrType, Type dataContractType) {
return null;
}
public void GetKnownCustomDataTypes(Collection<Type> customDataTypes) {
throw new NotImplementedException();
}
public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData) {
throw new NotImplementedException();
}
public CodeTypeDeclaration ProcessImportedType(CodeTypeDeclaration typeDeclaration, CodeCompileUnit compileUnit) {
throw new NotImplementedException();
}
}
其唯一目的是说 ProductId
类型的数据协定实际上是 string
。
然后您可以使用此代理生成模式:
var exporter = new XsdDataContractExporter();
exporter.Options = new ExportOptions();
exporter.Options.DataContractSurrogate = new ProductIdSurrogate();
exporter.Export(typeof(TestTypeOne));
您可以使用这种方法进行序列化本身,但我发现它更复杂。
您可以阅读有关代理和 WCF 的更多信息 here,最底部有一个示例,说明如何将代理用于 WSDL 生成端点(部分 "To Use a surrogate for Metadata Export")。
我有一个公开类型 TestTypeOne
的 WCF 服务,该类型当前有一个名为 ProductId
:
public class TestTypeOne
{
[DataMember(Name = "ProductId")]
public string ProductId { get; set; } //note it's a string at the moment
}
我想将此 属性 的类型从 string
更改为名为 ProductId
的自定义值类型,但不破坏 WCF 合同(这仅适用于服务器端,客户端应该仍然将 ProductId
视为字符串。)
public class TestTypeOne
{
[DataMember(Name = "ProductId")]
public ProductId ProductId { get; set; }
}
自定义类型是这样的(为简洁起见删除了大部分代码):
public struct ProductId : IEquatable<ProductId>
{
readonly string productId;
public ProductId(string productId)
{
this.productId = productId
}
public override string ToString() => productId ?? string.Empty;
}
使用以下测试代码:
var sb = new StringBuilder();
using (var writer = new XmlTextWriter(new StringWriter(sb)))
{
var dto = new TestTypeOne {
ProductId = new ProductId("1234567890123")
};
var serializer = new DataContractSerializer(typeof(TestTypeOne));
serializer.WriteObject(writer, dto);
writer.Flush();
}
Console.WriteLine(sb.ToString());
序列化时的预期输出应该是:
<Scratch.TestTypeOne xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/Tests">
<ProductId>1234567890123</ProductId>
</Scratch.TestTypeOne>
我已经尝试实施 ISerializable
但这似乎只能让我控制 ProductId
xml 标签的内容,而不是标签本身(所以我可以制作 [= =22=] 发生)。
理想情况下,我希望可以对 ProductId
类型本身做一些事情,因为这种类型在许多地方和许多合同中都有使用。
您是否也尝试过将 DataContract/DataMember 属性添加到此 ProductId class?
即:
[DataContract]
public struct ProductId : IEquatable<ProductId>
{
[DataMember]
readonly string productId;
public ProductId(string productId)
{
this.productId = productId
}
public override string ToString() => productId ?? string.Empty;
}
此外,在这种情况下不需要名称 属性 (Name="ProductId"),因为变量名称与您覆盖它的名称相同。
我认为最简单的方法是实施 IXmlSerializable
:
public struct ProductId : IXmlSerializable
{
readonly string productId;
public ProductId(string productId)
{
this.productId = productId;
}
public override string ToString() => productId ?? string.Empty;
XmlSchema IXmlSerializable.GetSchema() {
return null;
}
void IXmlSerializable.ReadXml(XmlReader reader) {
this = new ProductId(reader.ReadString());
}
void IXmlSerializable.WriteXml(XmlWriter writer) {
writer.WriteString(this.productId);
}
}
要为这种情况调整 WCF xsd 生成(强制它生成 xs:string
)- 您可以使用数据合同代理来生成 xsd。例如你可以有这样的建议:
public class ProductIdSurrogate : IDataContractSurrogate {
public Type GetDataContractType(Type type) {
if (type == typeof(ProductId))
return typeof(string);
return type;
}
public object GetObjectToSerialize(object obj, Type targetType) {
throw new NotImplementedException();
}
public object GetDeserializedObject(object obj, Type targetType) {
throw new NotImplementedException();
}
public object GetCustomDataToExport(MemberInfo memberInfo, Type dataContractType) {
return null;
}
public object GetCustomDataToExport(Type clrType, Type dataContractType) {
return null;
}
public void GetKnownCustomDataTypes(Collection<Type> customDataTypes) {
throw new NotImplementedException();
}
public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData) {
throw new NotImplementedException();
}
public CodeTypeDeclaration ProcessImportedType(CodeTypeDeclaration typeDeclaration, CodeCompileUnit compileUnit) {
throw new NotImplementedException();
}
}
其唯一目的是说 ProductId
类型的数据协定实际上是 string
。
然后您可以使用此代理生成模式:
var exporter = new XsdDataContractExporter();
exporter.Options = new ExportOptions();
exporter.Options.DataContractSurrogate = new ProductIdSurrogate();
exporter.Export(typeof(TestTypeOne));
您可以使用这种方法进行序列化本身,但我发现它更复杂。
您可以阅读有关代理和 WCF 的更多信息 here,最底部有一个示例,说明如何将代理用于 WSDL 生成端点(部分 "To Use a surrogate for Metadata Export")。