Удалить пространства имен xml из WCF-ответа restful - PullRequest
18 голосов
/ 27 января 2009

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

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

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

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

public ResponseInfo Process(string input) {
}

Ответы [ 9 ]

19 голосов
/ 03 марта 2009

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

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

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

7 голосов
/ 06 января 2011

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

5 голосов
/ 05 февраля 2009

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

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

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

<ResponseInfo>

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

4 голосов
/ 14 марта 2009

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

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

3 голосов
/ 14 февраля 2012

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

2 голосов
/ 01 декабря 2011

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

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

http://mycompany.com/myapi/

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

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

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

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

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

1 голос
/ 04 сентября 2016

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

Добавьте к своему интерфейсу обслуживания:

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

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

[OperationContract(Action = "http://www.YourNameSpace.com/ActionName",ReplyAction = "http://www.YourNameSpace.com/ActionName")]
1 голос
/ 01 февраля 2009

В моем сервисе 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".

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...