Ответ 1
Вы можете удалить пространство имен XML, установив параметр Namespace атрибута DataContract в пустую строку, например:
[DataContract(Namespace = "")]
public class ResponseInfo
{
// ...
}
Надеюсь, это поможет...
Я использую WCF для возврата обычного старого документа XML (POX) вызывающему. Я использую форматтера XML Serializer, чтобы превратить объекты в XML.
В возвращенном документе у меня есть некоторые посторонние ссылки пространства имен xml (которых не было в версии ASMX) для XML-схемы и экземпляра. Я видел различные аргументы в Интернете, что они не должны быть удалены, которые я не покупаю для возврата простого XML-документа.
Каков самый простой способ удаления этих ссылок xmlns из возвращаемого XML-документа в WCF?
Подпись выглядит так:
public ResponseInfo Process(string input) {
}
Вы можете удалить пространство имен XML, установив параметр Namespace атрибута DataContract в пустую строку, например:
[DataContract(Namespace = "")]
public class ResponseInfo
{
// ...
}
Надеюсь, это поможет...
У меня была та же проблема. Добавление BodyStyle: = WebMessageBodyStyle.Bare к WebInvoke работало для меня. Ответ больше не завернут в метаданные.
Я предполагаю, что вы пытаетесь вместо этого получить что-то вроде этого в начале вашего xml:
<ResponseInfo
xmlns="http://schemas.datacontract.org/2004/07/ResponseInfo"
xmlns:i="http://www.w3.org/2001/XMLSchema-instance" >
Вы хотите просто:
<ResponseInfo>
К сожалению, я еще не видел легкий способ удалить эти поля. Я искал поисковые запросы, и большинство вариантов для его удаления требуют создания собственного инспектора сообщений или вашего собственного энкодера.
Если вы хотите изменить Xml, одним из способов является использование XslTransform. У меня был аналогичный случай, когда мне нужно было удалить атрибуты xmlns из запроса Xml Post.
В WCF вы можете "перехватывать" сообщения Xml до выхода или до того, как они будут обработаны на пути, путем внедрения либо IClientMessageInspector, либо IDispatchMessageInspector, в зависимости от того, нужно ли вам это на стороне клиента или на стороне сервера.
Например, чтобы удалить атрибуты пространства имен из исходящего XML-сообщения в веб-службу, я реализовал IClientMessageInspector, используя следующий код:
#region IClientMessageInspector Members
public void AfterReceiveReply(ref Message reply, object correlationState)
{
//Console.WriteLine(reply.ToString());
}
private XslCompiledTransform xt = null;
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
Console.WriteLine(request.ToString());
if (!request.IsEmpty)
{
XmlReader bodyReader =
request.GetReaderAtBodyContents().ReadSubtree();
MemoryStream ms = new MemoryStream();
XmlWriter xw = XmlWriter.Create(ms);
if (xt == null)
{
xt = new XslCompiledTransform(true);
xt.Load("StripXmlnsi.xslt");
}
xt.Transform(bodyReader, xw);
ms.Flush();
ms.Seek(0, SeekOrigin.Begin);
bodyReader = XmlReader.Create(ms);
Message changedMessage = Message.CreateMessage(request.Version, null, bodyReader);
changedMessage.Headers.CopyHeadersFrom(request.Headers);
changedMessage.Properties.CopyProperties(request.Properties);
request = changedMessage;
}
return null;
}
#endregion
и использовал следующее преобразование:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="*">
<!-- remove element prefix (if any) -->
<xsl:element name="{local-name()}">
<!-- process attributes -->
<xsl:for-each select="@*">
<!-- remove attribute prefix (if any) -->
<xsl:attribute name="{local-name()}">
<xsl:value-of select="." />
</xsl:attribute>
</xsl:for-each>
<xsl:apply-templates />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Надеюсь, что это полезно.
Я нашел хорошее решение этой проблемы, которое позволяет вставлять собственный XmlSerializer в WCF, который используется при сериализации и десериализации запросов. Этот XmlSerializer можно настроить так, чтобы опускать пространства имен XML полностью (включая xmlns:i="w3.org/2001/XMLSchema-instance"
) или любым другим способом, который вы желаете.
В моем решении используется WcfRestContrib. Вы могли бы использовать только формат POX, но в нашем случае мы хотели поддерживать атрибуты, поэтому мы написали собственный простой форматировщик.
Инструкция:
1) Ссылка WcfRestContrib из вашего проекта.
2) Создайте реализацию IWebFormatter
:
public class NamespacelessXmlFormatter : IWebFormatter {
public object Deserialize(WebFormatterDeserializationContext context, Type type) {
if (context.ContentFormat != WebFormatterDeserializationContext.DeserializationFormat.Xml) {
throw new InvalidDataException("Data must be in xml format.");
}
return NamespacelessXmlSerializer.Deserialize(context.XmlReader, type);
}
public WebFormatterSerializationContext Serialize(object data, Type type) {
using (var stream = NamespacelessXmlSerializer.Serialize(data, type)) {
using (var binaryReader = new BinaryReader(stream)) {
byte[] bytes = binaryReader.ReadBytes((int)stream.Length);
return WebFormatterSerializationContext.CreateBinary(bytes);
}
}
}
}
Использует XmlSerializer, который соответствует вашим потребностям (здесь наш, который просто пропускает все пространства имен):
public static class NamespacelessXmlSerializer {
private static readonly XmlSerializerNamespaces _customNamespace = new XmlSerializerNamespaces();
private static readonly XmlWriterSettings _xmlSettings = new XmlWriterSettings {
OmitXmlDeclaration = true
};
static NamespacelessXmlSerializer() {
// to make sure .NET serializer doesn't add namespaces
_customNamespace.Add(String.Empty, String.Empty);
}
/// <summary>
/// Deserializes object from its XML representation.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="stream"></param>
/// <returns></returns>
public static T Deserialize<T>(Stream stream) {
return (T)Deserialize(stream, typeof(T));
}
/// <summary>
/// Deserializes object from its XML representation.
/// </summary>
public static object Deserialize(Stream stream, Type type) {
var ds = new XmlSerializer(type);
var d = ds.Deserialize(stream);
return d;
}
public static object Deserialize(XmlDictionaryReader xmlReader, Type type) {
var ds = new XmlSerializer(type);
var d = ds.Deserialize(xmlReader);
return d;
}
/// <summary>
/// Serializes object to XML representation.
/// </summary>
/// <exception cref="InvalidOperationException">
/// Is thrown when there was an error generating XML document. This can happen
/// for example if the object has string with invalid XML characters:
/// http://www.w3.org/TR/2004/REC-xml-20040204/#charsets.
/// See this article for other potential issues:
/// http://msdn.microsoft.com/en-us/library/aa302290.aspx
/// </exception>
public static Stream Serialize<T>(T objectToSerialize) {
return Serialize(objectToSerialize, typeof(T));
}
/// <summary>
/// Serializes object to XML representation.
/// </summary>
/// <exception cref="InvalidOperationException">
/// Is thrown when there was an error generating XML document. This can happen
/// for example if the object has string with invalid XML characters:
/// http://www.w3.org/TR/2004/REC-xml-20040204/#charsets.
/// See this article for other potential issues:
/// http://msdn.microsoft.com/en-us/library/aa302290.aspx
/// </exception>
public static Stream Serialize(object objectToSerialize, Type type) {
var stream = new MemoryStream();
XmlWriter writer = XmlWriter.Create(stream, _xmlSettings);
var x = new XmlSerializer(type);
x.Serialize(writer, objectToSerialize, _customNamespace);
stream.Position = 0;
return stream;
}
}
3) Примените атрибуты WebDispatchFormatter...
к своей службе, используя вашу собственную реализацию в качестве типа (на основе этой документации):
[WebDispatchFormatterConfiguration("application/xml")]
[WebDispatchFormatterMimeType(typeof(NamespacelessXmlFormatter), "application/xml")]
4) Примените атрибут WebDispatchFormatter
ко всем вашим методам обслуживания (на основе этой документации).
5) Что это. Проверьте свою службу и подтвердите, что она ведет себя так, как ожидалось.
Просто чтобы дать другую перспективу, если пространство имен уникально для вашего проекта, например:
то его следует сохранить.
Такие пространства имен являются наилучшей практикой, они добавят менее 1 строки шаблона к любым вызовам XPath (которые вы можете превратить в вспомогательный метод) и потребуете около 15 строк вспомогательного кода для создания карты префикса /URI, но это единственный недостаток, и вы не всегда будете сталкиваться с ним.
Взамен вы получаете однозначные имена для каждого элемента, и это означает, что вы можете создавать пространства имен сторонних организаций с безнаказанностью, например. в теории вы можете сразу вернуть XHTML без кодирования уровня приложения.
Другие появляющиеся пространства имен вряд ли будут проблемой, поскольку они вряд ли будут использоваться ни в одном из тегов, которые вы определили в своем проекте. На самом деле, если они есть, тогда есть ошибка. Вероятное объяснение существования заключается в том, что структура добавила их только в том случае, если ему нужно было добавить элемент где-то ниже того места, где они объявлены, что может не быть местоположением, о котором вы должны заботиться.
В другом ответе упоминается http://www.w3.org/2001/XMLSchema-instance, который немного особенный, возможно, вы могли бы сказать, какие пространства имен были добавлены.
Не уверен, что это поможет, но у нас была аналогичная проблема. Вместо того, чтобы украшать тысячи элементов данных атрибутами DataContract/DataMember и использовать (по умолчанию) DataContractSerializer, мы обнаружили, что если наша служба WCF использовала XmlSerializerFormat, мы могли бы легко десериализовать наши объекты.
[System.ServiceModel.ServiceContract]
public interface IRestService
{
[System.ServiceModel.OperationContract]
// Added this attribute to use XmlSerializer instead of DataContractSerializer
[System.ServiceModel.XmlSerializerFormat(
Style=System.ServiceModel.OperationFormatStyle.Document)]
[System.ServiceModel.Web.WebGet(
ResponseFormat = System.ServiceModel.Web.WebMessageFormat.Xml,
UriTemplate = "xml/objects/{myObjectIdentifier}")]
MyObject GetMyObject(int myObjectIdentifier);
}
Вот как мы десериализируем объекты:
public static T DeserializeTypedObjectFromXmlString<T>(string input)
{
T result;
try
{
System.Xml.Serialization.XmlSerializer xs = new System.Xml.Serialization.XmlSerializer(typeof(T));
using (System.IO.TextReader textReader = new System.IO.StringReader(input))
{
result = (T)xs.Deserialize(textReader);
}
}
catch
{
throw;
}
return result;
}
В моей службе RESTful WCF, которую я написал до запуска стартового набора WCF RESTful, я сделал следующее, которое дает мне хорошие, чистые результаты.
Сначала убедитесь, что установлено поведение конечной точки webHttp:
<endpointBehaviors>
<behavior name="Web">
<webHttp/>
</behavior>
</endpointBehaviors>
Ваша конечная точка должна выглядеть примерно так (за вычетом договора для простоты):
<endpoint address="" binding="webHttpBinding" behaviorConfiguration="Web" />
В моем контракте на обслуживание есть следующие операционные контракты:
[WebGet(UriTemplate="tasks", ResponseFormat=WebMessageFormat.Xml )]
[OperationContract]
Task[] GetTasks();
[WebGet(UriTemplate="tasks/{id}", ResponseFormat=WebMessageFormat.Xml)]
[OperationContract]
Task GetTask(string id);
Сама реализация сервиса ничего особенного не имеет. Вы можете попробовать изменить WebMessageFormat, но единственным элементом в перечислении является "json".
У меня такая же проблема, когда я работаю с клиентами ASMX, и для меня это решает проблему:
Добавьте в свой сервисный интерфейс:
[XmlSerializerFormat(Use = OperationFormatUse.Literal, Style = OperationFormatStyle.Document)]
Добавить в операции:
[OperationContract(Action = "http://www.YourNameSpace.com/ActionName",ReplyAction = "http://www.YourNameSpace.com/ActionName")]