简体   繁体   English

用于自定义值类型的DataContractSerializer

[英]DataContractSerializer for custom Value types

I have a WCF service which exposes a type TestTypeOne which currently has a string property called ProductId : 我有一个WCF服务,它公开一个类型TestTypeOne ,该类型当前具有一个称为ProductId的字符串属性:

public class TestTypeOne
{
    [DataMember(Name = "ProductId")]
    public string ProductId { get; set; } //note it's a string at the moment
}

I want to change the type of this property from string to a custom value type called ProductId , but without breaking the WCF contract (this is only for the server side, the clients should see the ProductId as a string still.) 我想将此属性的类型从string更改为名为ProductId的自定义值类型,但又不破坏WCF合同(这仅适用于服务器端,客户端仍应将ProductId视为字符串。)

public class TestTypeOne
{
    [DataMember(Name = "ProductId")]
    public ProductId ProductId { get; set; }
}

The custom type is something like this (most code removed for brevity): 自定义类型是这样的(为简洁起见,删除了大多数代码):

public struct ProductId : IEquatable<ProductId>
{
    readonly string productId;

    public ProductId(string productId)
    {
        this.productId = productId
    }

    public override string ToString() => productId ?? string.Empty;
}

Using the following test code: 使用以下测试代码:

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());

The expected output when serializing should be: 序列化时的预期输出应为:

<Scratch.TestTypeOne xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/Tests">
    <ProductId>1234567890123</ProductId>
</Scratch.TestTypeOne>

I have tried implementing ISerializable but that seems to only let me control the content of the ProductId xml tag, not the tag itself (so I can make things like <ProductId><something>1234113</something></ProductId> happen). 我尝试实现ISerializable但这似乎只能让我控制ProductId xml标记的内容,而不是标记本身(因此,可以使<ProductId><something>1234113</something></ProductId>这样的事情发生)。

Ideally I am after something I can do to the ProductId type itself, as this type is used in many places, and many contracts. 理想情况下,我可以对ProductId类型本身做一些事情,因为该类型在许多地方和许多合同中都使用过。

I think easiest way would be to implement IXmlSerializable : 我认为最简单的方法是实现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);
    }
}

To adjust WCF xsd generation for this case (force it to generate xs:string ) - you can use data contract surrogate for xsd generation. 要针对这种情况调整WCF xsd的生成(强制使其生成xs:string )-您可以使用数据协定代理来生成xsd。 For example you can have such 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();
    }
}

Whose only purpose is say that data contract for ProductId type is really string . 谁的唯一目的是说ProductId类型的数据协定实际上是string

Then you can use this surrogate for schema generation: 然后,您可以使用此代理进行模式生成:

var exporter = new XsdDataContractExporter();
exporter.Options = new ExportOptions();
exporter.Options.DataContractSurrogate = new ProductIdSurrogate();
exporter.Export(typeof(TestTypeOne));

You can use this approach for serialization itself, but I find it more complicated. 您可以将这种方法用于序列化本身,但是我发现它更加复杂。

You can read more info about surrogates and WCF here , and the very bottom there is an example of how you can use surrogate for WSDL generation endpoint (section "To Use a surrogate for Metadata Export"). 您可以在此处阅读有关代理和WCF的更多信息,并且在最底端有一个示例,说明如何将代理用于WSDL生成端点(“为元数据导出使用代理”部分)。

Have you tried adding the DataContract/DataMember attributes to this ProductId class as well? 您是否也尝试过将DataContract / DataMember属性添加到此ProductId类?

ie: 即:

[DataContract]
public struct ProductId : IEquatable<ProductId>
{

[DataMember]
readonly string productId;

public ProductId(string productId)
{
    this.productId = productId
}

public override string ToString() => productId ?? string.Empty;

}

Also, the name property (Name="ProductId") in this case is not needed, as the variable name is the same as the one you're overriding it with. 另外,在这种情况下,不需要名称属性(Name =“ ProductId”),因为变量名称与您要覆盖它的名称相同。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM