Добавление нескольких пространств имен в объект ответа 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>
обратите внимание на урну:bccdx.ca с префиксом
Мне интересно, как, если вообще мы можем добавить более одного пространства имен с префиксами к результирующему сериализованному сообщению XML через контракты? Я видел в Интернете намеки на переопределение сериализатора по умолчанию, но я бы предпочел этого не делать. Конечно, об этом уже думали и разбирались?
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 с пространством имен в корневом элементе по желанию.