c# - 用于自定义值类型的 DataContractSerializer
问题描述
我有一个 WCF 服务,它公开了一个类型,该类型TestTypeOne
当前具有一个名为的字符串属性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 标签的内容,而不是标签本身(所以我可以让事情<ProductId><something>1234113</something></ProductId>
发生)。
理想情况下,我追求的是我可以对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。例如,你可以有这样的 suggorate:
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的更多信息,最底部有一个示例,说明如何将代理用于 WSDL 生成端点(“使用代理进行元数据导出”部分)。
推荐阅读
- c++ - 具有方法重载的接口类?
- javascript - 如何使用 Jest 在函数内测试类实例
- loops - Dr-Racket 中的背景声音 [lang #r5rs]
- c++ - FLAC.framework 错误
- oracle - Oracle Cost-Based Optimizer:当标准在维度表上时如何使用事实表外键列的统计信息
- reactjs - 测试反应重定向
- android - 如何将 arrayList 传递给 Tasks.whenAllSuccess(arrayList)?
- regex - 使用正则表达式返回通配符(重复行)
- r - 如何使用 lapply 和复制在 R 中创建顺序排序的列表?
- mongodb - 您可以在单个聚合管道中按 id 查找多个文档吗?