DataContractSerializer для пользовательских типов значений - PullRequest
0 голосов
/ 03 мая 2018

У меня есть служба 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, так как этот тип используется во многих местах и ​​во многих контрактах.

Ответы [ 2 ]

0 голосов
/ 03 мая 2018

Я думаю, что проще всего было бы реализовать 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);
    }
}

Чтобы настроить генерацию xsd WCF для этого случая (принудительно сгенерировать 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 можно прочитать здесь здесь , а в самом низу приведен пример использования суррогата для конечной точки генерации WSDL (раздел «Использование суррогата для экспорта метаданных») .

0 голосов
/ 03 мая 2018

Вы пытались добавить атрибуты DataContract / DataMember и к этому классу ProductId?

т.е:

[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 (Name = "ProductId") в этом случае не требуется, так как имя переменной совпадает с именем, которым вы ее переопределяете.

...