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

17

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

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

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

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

public ResponseInfo Process(string input) {
}
    
задан Duncan 27.01.2009 в 11:11
источник

9 ответов

19

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

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

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

    
ответ дан Kevin Babcock 03.03.2009 в 02:45
источник
  • Это работает, но вам может потребоваться поместить атрибуты [DataMember] в поля, чтобы гарантировать, что xml не пуст –  Simon Munro 30.04.2009 в 16:31
  • Из моих тестов этот метод не избавляется от xmlns: i="w3.org/2001/XMLSchema-instance" объявление –  Darrel Miller 24.04.2010 в 16:05
7

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

    
ответ дан user565923 06.01.2011 в 19:59
источник
  • У меня был BodyStyl = WebMessageBodyStyle.Wrapper и сменил его на BodyStyle = WebMessageBodyStyle.Bare решил мою проблему. –  YoYoMyo 29.12.2011 в 23:47
  • Великий Скотт! это потрясающе. Спасибо за полезный тег! –  Nathan Tregillus 08.04.2012 в 01:22
  • Это должно быть намного больше. Отличный ответ. –  Eight-Bit Guru 17.05.2013 в 15:37
  • Это не работает для меня. –  crush 20.09.2013 в 16:05
  • Это не влияет на присутствие пространства имен –  Schneider 02.06.2016 в 08:46
5

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

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

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

<ResponseInfo>

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

    
ответ дан christophercotton 05.02.2009 в 19:36
источник
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>

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

    
ответ дан Bernie 14.03.2009 в 08:37
источник
  • Это ужасное решение. Вы действительно думаете, что это нормально, чтобы написать весь этот код, просто чтобы удалить пространство имен? –  user1662812 03.10.2012 в 14:35
  • Да, малый размер не равен красоте. Если у вас есть более простое решение, пожалуйста, не стесняйтесь публиковать его, но просто увольняйте что-то, потому что он занимает более 3 строк кода, а не то, что будет делать достойный инженер. –  Bernie 01.03.2013 в 21:34
3

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

    
ответ дан Lawrence Johnston 13.02.2012 в 21:07
источник
  • Это похоже на ответ. –  Melbourne Developer 26.10.2017 в 00:41
2

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

Ссылка

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

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

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

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

В другом ответе упоминается Ссылка , которая немного особенная, возможно, вы могли бы сказать, какие пространства имен были добавлены.     

ответ дан Simon Gibbs 14.04.2009 в 11:51
источник
2

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

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

    
ответ дан Sailing Judo 01.02.2009 в 16:19
источник
  • Было бы неплохо, если бы downvoter оставил бы комментарий. Мой ответ на 100% правильный и работоспособный. Возможно, это не соответствует тому, что задает человек, или, может быть, есть лучший способ, но он, безусловно, работает. –  Sailing Judo 01.03.2010 в 17:00
  • я не был нисходящим, но хотя ваш ответ может быть «правильным», он не отвечает на этот конкретный вопрос. –  jm. 17.04.2014 в 04:04
0

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

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

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

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

[OperationContract(Action = "http://www.YourNameSpace.com/ActionName",ReplyAction = "http://www.YourNameSpace.com/ActionName")]
    
ответ дан Qais Khateeb 04.09.2016 в 09:49
источник