Подтвердить что ты не робот

Удалите пространства имен xml из остаточного ответа WCF

Я использую WCF для возврата обычного старого документа XML (POX) вызывающему. Я использую форматтера XML Serializer, чтобы превратить объекты в XML.

В возвращенном документе у меня есть некоторые посторонние ссылки пространства имен xml (которых не было в версии ASMX) для XML-схемы и экземпляра. Я видел различные аргументы в Интернете, что они не должны быть удалены, которые я не покупаю для возврата простого XML-документа.

Каков самый простой способ удаления этих ссылок xmlns из возвращаемого XML-документа в WCF?

Подпись выглядит так:

public ResponseInfo Process(string input) {
}
4b9b3361

Ответ 1

Вы можете удалить пространство имен XML, установив параметр Namespace атрибута DataContract в пустую строку, например:

[DataContract(Namespace = "")]
public class ResponseInfo
{
    // ...
}

Надеюсь, это поможет...

Ответ 2

У меня была та же проблема. Добавление BodyStyle: = WebMessageBodyStyle.Bare к WebInvoke работало для меня. Ответ больше не завернут в метаданные.

Ответ 3

Я предполагаю, что вы пытаетесь вместо этого получить что-то вроде этого в начале вашего xml:

<ResponseInfo 
   xmlns="http://schemas.datacontract.org/2004/07/ResponseInfo"
   xmlns:i="http://www.w3.org/2001/XMLSchema-instance" >

Вы хотите просто:

<ResponseInfo>

К сожалению, я еще не видел легкий способ удалить эти поля. Я искал поисковые запросы, и большинство вариантов для его удаления требуют создания собственного инспектора сообщений или вашего собственного энкодера.

Ответ 4

Если вы хотите изменить 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>

Надеюсь, что это полезно.

Ответ 5

Я нашел хорошее решение этой проблемы, которое позволяет вставлять собственный 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) Что это. Проверьте свою службу и подтвердите, что она ведет себя так, как ожидалось.

Ответ 6

Просто чтобы дать другую перспективу, если пространство имен уникально для вашего проекта, например:

http://mycompany.com/myapi/

то его следует сохранить.

Такие пространства имен являются наилучшей практикой, они добавят менее 1 строки шаблона к любым вызовам XPath (которые вы можете превратить в вспомогательный метод) и потребуете около 15 строк вспомогательного кода для создания карты префикса /URI, но это единственный недостаток, и вы не всегда будете сталкиваться с ним.

Взамен вы получаете однозначные имена для каждого элемента, и это означает, что вы можете создавать пространства имен сторонних организаций с безнаказанностью, например. в теории вы можете сразу вернуть XHTML без кодирования уровня приложения.

Другие появляющиеся пространства имен вряд ли будут проблемой, поскольку они вряд ли будут использоваться ни в одном из тегов, которые вы определили в своем проекте. На самом деле, если они есть, тогда есть ошибка. Вероятное объяснение существования заключается в том, что структура добавила их только в том случае, если ему нужно было добавить элемент где-то ниже того места, где они объявлены, что может не быть местоположением, о котором вы должны заботиться.

В другом ответе упоминается http://www.w3.org/2001/XMLSchema-instance, который немного особенный, возможно, вы могли бы сказать, какие пространства имен были добавлены.

Ответ 7

Не уверен, что это поможет, но у нас была аналогичная проблема. Вместо того, чтобы украшать тысячи элементов данных атрибутами 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;
}

Ответ 8

В моей службе 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".

Ответ 9

У меня такая же проблема, когда я работаю с клиентами ASMX, и для меня это решает проблему:

Добавьте в свой сервисный интерфейс:

[XmlSerializerFormat(Use = OperationFormatUse.Literal, Style = OperationFormatStyle.Document)]

Добавить в операции:

[OperationContract(Action = "http://www.YourNameSpace.com/ActionName",ReplyAction = "http://www.YourNameSpace.com/ActionName")]