Добавление нескольких пространств имен в объект ответа MessageContract WCF (MessageBodyMember)

У нас есть установка WCF со следующими контрактами:

    [ServiceContract(
    Namespace = Constants.Namespaces.HL7Namespace,
    Name = Constants.Roles.ContentRequiredDocumentManagementSystem)]
// XmlSerializerFormat is needed to expose the HL7 schema fields without the "Field" suffix on each one, eg: idField
[XmlSerializerFormat]
public interface ICDARequest
{
    [OperationContract(
        // wsdl request action
        Action = Constants.Namespaces.HL7Namespace + ":" + Constants.Interactions.RCMR_IN000029UV01 + "." + Constants.VersionType.NormativeCode + Constants.Version.InteractionVersion,
        // wsdl operation name
        Name = Constants.Interactions.RCMR_IN000029UV01,
        // wsdl response action
        ReplyAction = Constants.Namespaces.HL7Namespace + ":" + Constants.Interactions.RCMR_IN000030UV01 + "." + Constants.VersionType.NormativeCode + Constants.Version.InteractionVersion)]
    SearchMessagesResponse SearchMessages(SearchMessagesRequest RCMR_IN000029UV01);


    [MessageContract(
        IsWrapped = false]
    public class SearchMessagesResponse
    {
        [MessageBodyMember(
            Name = State.Constants.Interactions.RCMR_IN000030UV01,
            Namespace = State.Constants.Namespaces.HL7Namespace)]
        public RCMR_IN000030UV01 data;
    }
}
  • Они основаны на классах, созданных на основе схемы HL7v3 с использованием xsd.exe.
  • Затем мы изменили схему, чтобы добавить пользовательский элемент, используя собственное пространство имен, чтобы различать его, и заново сгенерировали классы.
  • это работало нормально.

Он добавил:

[System.Xml.Serialization.XmlTypeAttribute(TypeName = "BCCDX.DistributionStatus", Namespace = "urn:bccdx.ca")]
public partial class BCCDXDistributionStatus
{
    [System.Xml.Serialization.XmlElementAttribute("receivedTime", Namespace = "urn:bccdx.ca", IsNullable = false)]
    public TS receivedTime{...}
}

что и хотелось.

Затем в службе WCF мы можем использовать новый класс и члены:

var distStatus = new BCCDXDistributionStatus();
distStatus.receivedTime = CreateTS(locStat.MessageDownloadDate);

затем это сериализуется и отправляется по сети, выглядя так:

<distributionStatus xmlns="urn:bccdx.ca">
    <receivedTime value="201702150956-0800"/>
</distributionStatus>

что почти правильно. Загвоздка связана с тем, что XML-документ не имеет ссылки на пространство имен "urn:bccdx.ca". Я предполагал, что он будет автоматически добавлен в корневой элемент документа при сериализации, но ошибся. Вот как это выглядит в итоге:

<RCMR_IN000030UV01 ITSVersion="XML_1.0" xmlns="urn:hl7-org:v3">
...
</RCMR_IN000030UV01>

когда то, что действительно желательно, выглядит примерно так:

<RCMR_IN000030UV01 ITSVersion="XML_1.0" xmlns="urn:hl7-org:v3" xmlns:x="urn:bccdx.ca">
...
</RCMR_IN000030UV01>

обратите внимание на urn:bccdx.ca с префиксом

Мне интересно, как, если мы вообще можем добавить более одного пространства имен с префиксами к результирующему сериализованному XML-сообщению через контракты? Я видел в Интернете намеки на переопределение сериализатора по умолчанию, но я бы не хотел. Наверняка об этом уже думали и решали раньше?


person Bensonius    schedule 17.02.2017    source источник
comment
1) Можно ли каким-либо образом расширить это до минимально воспроизводимого примера или, по крайней мере, чего-то, что можно скомпилировать , без пропущенных типов? 2) Вы говорите, что использовали xsd.exe для создания своих классов, вы где-то применяете [XmlSerializerFormat]? Я не вижу этого в вопросе.   -  person dbc    schedule 24.02.2017
comment
Спасибо, да, я отредактировал, добавив XmlSerializerFormat к исходному вопросу.   -  person Bensonius    schedule 24.02.2017


Ответы (1)


Во-первых, я собираюсь предположить, что где-то в вашем сервисном контракте вы указываете использование XmlSerializer, используя атрибут [XmlSerializerFormat], например так:

[ServiceContract()]
[XmlSerializerFormat]
public interface IService1
{
    [OperationContract(
        // wsdl request action
        Action = Constants.Namespaces.HL7Namespace + ":" + Constants.Interactions.RCMR_IN000029UV01 + "." + Constants.VersionType.NormativeCode + Constants.Version.InteractionVersion,
        // wsdl operation name
        Name = Constants.Interactions.RCMR_IN000029UV01,
        // wsdl response action
        ReplyAction = Constants.Namespaces.HL7Namespace + ":" + Constants.Interactions.RCMR_IN000030UV01 + "." + Constants.VersionType.NormativeCode + Constants.Version.InteractionVersion)]
    SearchMessagesResponse SearchMessages(/* SearchMessagesRequest RCMR_IN000029UV01*/);
}

Хотя это не упоминается в вашем вопросе, если бы вы этого не делали, то объявления атрибутов [System.Xml.Serialization.XmlElementAttribute(...)] в ваших типах не имели бы никакого эффекта, поскольку они игнорируются DataContractSerializer.

Во-вторых, я собираюсь предположить, что ваш тип RCMR_IN000030UV01 в настоящее время выглядит примерно так:

[XmlRoot(ElementName = "RCMR_IN000030UV01", Namespace = "urn:hl7-org:v3")]
public partial class RCMR_IN000030UV01
{
    // The initially auto-generated code
    [XmlAttribute(AttributeName = "ITSVersion")]
    public string ITSVersion { get; set; }
}

public partial class RCMR_IN000030UV01
{
    // The added property
    [System.Xml.Serialization.XmlElementAttribute("distributionStatus", Namespace = "urn:bccdx.ca", IsNullable = false)]
    public BCCDXDistributionStatus distStatus { get; set; }
}

[System.Xml.Serialization.XmlTypeAttribute(TypeName = "BCCDX.DistributionStatus", Namespace = "urn:bccdx.ca")]
public partial class BCCDXDistributionStatus
{
    [System.Xml.Serialization.XmlElementAttribute("receivedTime", Namespace = "urn:bccdx.ca", IsNullable = false)]
    public TS receivedTime { get; set; }
}

public class TS
{
    [XmlAttribute("value")]
    public DateTime Value { get; set; }
}

В настоящее время ваша служба возвращает XML, который выглядит следующим образом:

<RCMR_IN000030UV01 ITSVersion="1.0"
    xmlns="urn:hl7-org:v3"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <distributionStatus
        xmlns="urn:bccdx.ca">
        <receivedTime value="2017-02-23T00:00:00-05:00"/>
    </distributionStatus>
</RCMR_IN000030UV01>

Но вы хотите это:

<RCMR_IN000030UV01 ITSVersion="1.0"
    xmlns="urn:hl7-org:v3"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    <!---This should be added ---->
    xmlns:x="urn:bccdx.ca">
    <!---And distributionStatus should be prefixed with x: ---->
    <x:distributionStatus>
        <x:receivedTime value="2017-02-23T00:00:00-05:00"/>
    </x:distributionStatus>
</RCMR_IN000030UV01>

Во-первых, отмечу, что эти два XML-файла семантически идентичны. В первом случае пространство имен "urn:bccdx.ca" объявляется пространством имен по умолчанию для самого нижнего элемента, в котором оно действительно необходимо. Во втором он определяется префиксом в начале файла. В любом случае элемент <distributionStatus> и его дочерние элементы попадают в правильное пространство имен.

Таким образом, вы можете просто принять XML как правильный.

Если по какой-то причине вы должны, чтобы пространство имен появлялось в начале XML с префиксом x:, вы можете добавить [XmlNamespaceDeclarations] к вашему RCMR_IN000030UV01, чтобы ваше пространство имен было объявлено на более высоком уровне:

public partial class RCMR_IN000030UV01
{
    [XmlNamespaceDeclarations]
    public XmlSerializerNamespaces xmlsn
    {
        get
        {
            var ns = new XmlSerializerNamespaces();
            ns.Add("x", "urn:bccdx.ca");
            return ns;
        }
        set
        {
            // Do nothing - fake property.
        }
    }
}

Как поясняется в документах, этот атрибут

Указывает, что целевое свойство, параметр, возвращаемое значение или член класса содержит префиксы, связанные с пространствами имен, которые используются в XML-документе.

Теперь ваша служба должна вернуть XML с пространством имен в корневом элементе по желанию.

person dbc    schedule 24.02.2017
comment
Это отличный ответ, все предположения неверны (я отредактировал исходный вопрос, включив в него объявление XmlSerializerFormat). Я добавил предложенное свойство [XmlNamespaceDeclarations], и оно сработало. Я согласен, что XML в обоих случаях правильный, но часть спецификации должна была быть именно такой. - person Bensonius; 24.02.2017