3

I have a series of objects that look like this:

namespace MyNamespace
{
  [DataContract(Namespace="")]
  public class MyClass1
  {
    [DataMember]
    public string MyProperty {get;set;}
  }
}

I have a method which exposes the WebInvoke that looks like this (very simplified as this actually does nothing right now, but still works for this test)

[WebInvoke(UriTemplate = "", Method="POST")]
public MyNamespace.MyClass1 GetItem(MyClass1 postedItem) { return postedItem; }

I would really like to be able to accept XML that looks like this:

<MyClass1>
  <MyProperty>1</MyProperty>
</MyClass1>

or this:

<MyClass1 xmlns:"http://schemas.datacontract.org/2004/07/MyNamespace">
  <MyProperty>1</MyProperty>
</MyClass1>

But so far my research seems to indicate this is not possible. My only idea right now is to use a IDispatchMessageInspector and consume the message, remove the xmlns namespace, and then allow WCF to continue processing the message. I have not had a lot of luck with this however, because once I consume the message it is no longer available for WCF to consume and deserialize.

Is there an easier way? Is there a better way?

4 Answers 4

1

You can use the dispatcher, but once you consume the message, you'll need to recreate it before returning from the method. The code below shows an example of it.

public class StackOverflow_7506072
{
    [DataContract(Name = "MyClass1", Namespace = "")]
    public class MyClass1
    {
        [DataMember]
        public string MyProperty { get; set; }
    }
    [ServiceContract]
    public class Service
    {
        [WebInvoke(UriTemplate = "", Method = "POST")]
        public MyClass1 GetItem(MyClass1 postedItem) { return postedItem; }
    }
    public class MyInspector : IEndpointBehavior, IDispatchMessageInspector
    {
        public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
        {
        }

        public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
        {
        }

        public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
        {
            endpointDispatcher.DispatchRuntime.MessageInspectors.Add(this);
        }

        public void Validate(ServiceEndpoint endpoint)
        {
        }

        public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
        {
            MemoryStream ms = new MemoryStream();
            XmlWriter w = XmlWriter.Create(ms);
            request.WriteMessage(w);
            w.Flush();
            ms.Position = 0;
            XElement element = XElement.Load(ms);
            if (element.Name.NamespaceName == "http://schemas.datacontract.org/2004/07/MyNamespace")
            {
                element.Name = XName.Get(element.Name.LocalName, "");
                foreach (XElement child in element.Descendants())
                {
                    if (child.Name.NamespaceName == "http://schemas.datacontract.org/2004/07/MyNamespace")
                    {
                        child.Name = XName.Get(child.Name.LocalName, "");
                    }
                }

                element.Attribute("xmlns").Remove();
            }

            XmlReader r = element.CreateReader();
            Message newRequest = Message.CreateMessage(r, int.MaxValue, request.Version);
            newRequest.Properties.CopyProperties(request.Properties);
            request = newRequest;
            return null;
        }

        public void BeforeSendReply(ref Message reply, object correlationState)
        {
        }
    }
    public static void Test()
    {
        string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
        ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
        ServiceEndpoint endpoint = host.AddServiceEndpoint(typeof(Service), new WebHttpBinding(), "");
        endpoint.Behaviors.Add(new WebHttpBehavior());
        endpoint.Behaviors.Add(new MyInspector());
        host.Open();
        Console.WriteLine("Host opened");

        WebClient c = new WebClient();
        c.Headers[HttpRequestHeader.ContentType] = "text/xml";
        string xml = "<MyClass1><MyProperty>123</MyProperty></MyClass1>";
        Console.WriteLine(c.UploadString(baseAddress + "/", xml));

        c = new WebClient();
        c.Headers[HttpRequestHeader.ContentType] = "text/xml";
        xml = "<MyClass1 xmlns=\"http://schemas.datacontract.org/2004/07/MyNamespace\"><MyProperty>123</MyProperty></MyClass1>";
        Console.WriteLine(c.UploadString(baseAddress + "/", xml));

        Console.Write("Press ENTER to close the host");
        Console.ReadLine();
        host.Close();
    }
}
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks, that is exactly what I needed. I will implement your suggestion.
0

This is a horrible idea. You're treating XML as though the standard doesn't matter.

The element MyClass1 in the "http://schemas.datacontract.org/2004/07/MyNamespace" namespace is not the same as the element MyClass in the default namespace.

Comments

0

You could also use WcfRestContrib to inject a custom XmlSerializer which supports this behavior. An example which uses an XmlSerializer that always omits namespaces entirely is here. You'll need to work out how to update the XmlSerializer to optionally support namespaces for deserialization.

Comments

0

If you set Namespace for service to Empty, you do not need to pass xmlns. But you need root xml tag matching to method name i.e.

<GetItem>
<MyClass1>
  <MyProperty>1</MyProperty>
</MyClass1>
</GetItem>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.