xml deserialization <string> tag - c#

I wrote web service using System.ServiceModel System.ServiceModel.Web System.ServiceModel.Activation namespaces.
[XmlSerializerFormat]
[OperationContract]
[WebGet(UriTemplate = Routing.GetClientRoute, BodyStyle = WebMessageBodyStyle.Bare)]
string GetClientNameById(string Id);
I described data with this class:
[XmlRoot("ServiceXmlReply")]
public class ServiceXmlReply
{
[XmlElement]
public string Name;
}
Problem is with respond, which i receive from service. It looks like this:
"<?xml version=\"1.0\" encoding=\"utf-8\"?>
<string>
<ServiceXmlReply xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"
xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">
<Name>Igor<Name>
<ServiceXmlReply>
</string>"
As can you see there not supposed to be "string" tag, but it is. Because of it i can't deserialize respond and get data.
Class is same on server and client's side.

Your operation contract defines that the response of GetClientNameById is of type string. So what happens is that the contents of your return type string is a serialized xml.
To get this working, you should is:
change the return type of your operation to ServiceXmlReply or
abandon the use of ServiceXmlReply. Use string in stead on service and client side.

Related

Return simple node in C# Rest Service

Sorry if this has already been asked but I couldn't find a solution anywhere.
I've got a Rest service developped in C#.
For now, I've got a resource with a contract looking like this
[OperationContract]
[WebInvoke(Method = "GET", ResponseFormat = WebMessageFormat.Xml, UriTemplate = "/tests)]
Test TestGet();
I'm migrating from an existing so I have to return the exact same XML than currently existing. Something like this :
<?xml version="1.0"?>
<test>OK</test>
I would like to return an object or a string but keep the result as an XML result and not a raw result.
How could I do this ?
If I return a String, I get this result :
<?xml version="1.0"?>
<string xmlns="http://schemas.microsoft.com/2003/10/Serialization/">KO</string>
If I return this object :
[DataContract]
public class Test
{
[DataMember(Name = "Test")]
public string Result { get; set; }
}
I get something like this :
<?xml version="1.0"?>
<Test xmlns="http://mynamespace">
<Test>OK</Test>
</Test>
How could I return my answer in the expected format ? Do I have to return a raw text or can I do something having XML as response ?
Thanks for those who will try to help.
I just found how to do it. If someone has the same question there is the answer :
[DataContract(IsWrapped = false)]
public class Test
{
[DataMember(Name = "Test")]
public string Result { get; set; }
}
The IsWrapped attribute will prevent to wrap the body. It is kinda obvious now that I know it but it took me much time.

XMLElement only contains first child node

I have a WCF service that getting sent XML files where the nodes can vary although the root elements are always the same. Some files may look like this:
<MyXML xmlns="something">
<xmlDoc>
<element1>Value</element1>
<element2>Value</element2>
</xmlDoc>
</MyXML>
While others look like this
<MyXML xmlns="something">
<xmlDoc>
<otherelement1>Value</otherelement1>
<child1>
<element3>Value</element3>
<element2>Value</element2>
</child1>
</xmlDoc>
</MyXML>
Because I have no way of knowing how these will look, what I wanted to do is just grab the whole element and then parse through it but right now I can only get the first child element (element1). My service interface looks like this:
[ServiceContract]
public interface IRestServiceImpl
{
[OperationContract]
[WebInvoke(Method = "POST",
ResponseFormat = WebMessageFormat.Xml,
RequestFormat = WebMessageFormat.Xml,
BodyStyle = WebMessageBodyStyle.Bare,
UriTemplate = "auth")]
ResponseData Auth(MyXML rData);
}
and my datacontract looks like this:
[DataContract(Namespace = "something")]
public class MyXML
{
[DataMember]
public XmlElement xmlDoc { get; set; }
}
But in my service I am only ever getting the first element from the XML -- ie
<element1>Value</element1>
So my question is, will this work? What do I need to change? I tried the data contract as a List instead
[DataContract(Namespace = "something")]
public class MyXML
{
[DataMember]
public List<XmlElement> xmlDoc { get; set; }
}
but that just returned an empty list. I am very new to WCF so let me know if any other details are needed.
Ok, turns out this was REALLY easy to workaround -- I`m actually writing the XML from an android app so I have the luxury of changing the structure. I simply added an inner node so my XML went from this
<MyXML xmlns="something">
<xmlDoc>
<otherelement1>Value</otherelement1>
<child1>
<element3>Value</element3>
<element2>Value</element2>
</child1>
</xmlDoc>
to this:
<MyXML xmlns="something">
<xmlDoc>
<xmlDocInner>
<otherelement1>Value</otherelement1>
<child1>
<element3>Value</element3>
<element2>Value</element2>
</child1>
</xmlDocInner>
</xmlDoc>
So now I get everything inside the xmlDocInner node when I get the xmlDoc element. It still begs the question of how one would get the entire element if there is no inner node as a buffer but I am ok with this as is.
To access the raw XML, use the Message class, not a DataContract. If you want a POCO to help you along, you are probably stuck with XmlSerializer.
static class SchemaConstants
{
public const string Namespace = "something";
}
[XmlType(Namespace = SchemaConstants.Namespace)]
[XmlRoot(Namespace = SchemaConstants.Namespace)]
public class xmlDoc
{
// case 1
public string element1;
public string element2;
// case 2
public string otherelement1;
public child1 child1;
}
[XmlType(Namespace = SchemaConstants.Namespace)]
public class child1
{
public string element3;
public string element2;
}
Any member not present will be deserialized as null. You will have to mark your contract or service implementation with the [XmlSerializerFormat] attribute.
Results for your first example:
Results for your second example:
You can also use a type which implements the IXmlSerializer interface and handle the deserialization yourself.

WCF: How to deserialize parameters of a SOAP message with different namespaces?

I am new to WCF and Stackoverflow. I am trying to handle a SOAP (1.2) request from an existing client.
The message would be like:
<s:Body>
<ns1:MyMethod>
<ns1:Parameter1> A string value </ns1:Parameter1>
<ns2:Parameter2> Another string value </ns2:Parameter2>
</ns1:MyMethod>
</s:Body>
Here is my server side code:
[SerivceContract(Namespace = "ns1...")]
public class IMyService
{
[OperationContract(Action="http://the action url")]
void MyMethod(string Parameter1, string Parameter2);
}
I can get "Parameter1" deserialized correctly, but "Parameter2" is always null. I suppose it was because of different namespaces (ns1 vs ns2).
Any helps?
It is highly unusual to have to create a service to fit the requests a client is already sending.
This is like trying to purchase a new computer which supports the drivers for a 15 year old printer.
Solution: just buy a new printer.
The client should be consuming the service, not the other way around.
Appreciate this does not directly answer your question and may be outside the scope of a solution.
This is an old question but I stumbled into it when trying to find the answer to a similar question.
The reason for which the Parameter1 was deserialized and not Parameter2 is probably because both fields had no namespace definition so they inherited the namespace from their parent (MyMethod). This is also explained on this thread.
For the current case, you would need to use the custom XmlSerializerFormat and add the ns2 namespace to the Param2:
[ServiceContract(Namespace = "http://ns1.com")]
[XmlSerializerFormat]
public interface IOpenInvoiceInterface
{
[OperationContract]
MyMethod Test(MyMethod req);
}
public class MyMethod
{
[MessageBodyMember]
public string Param1 { get; set; }
[MessageBodyMember(Namespace = "http://ns2.com")]
public string Param2 { get; set; }
}
With this setup, the following call will be deserialized as expected:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://ns1.com" xmlns:ns2="http://ns2.com">
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<ns1:MyMethod >
<ns1:Param1>abc</ns1:Param1>
<ns2:Param2>cde</ns2:Param2>
</ns1:MyMethod>
</s:Body>
</s:Envelope>

Null values for object properties de-serialized by WCF

I have a WCF web service that used to work fine. Somewhere down the line it stopped and I cant tell why. The code and the interface never changed nor did the web.config (at least not in relation to the web services section). I have a class:
[DataContract]
public class QuizServiceArgs
{
[DataMember(IsRequired = true, Order = 1)]
public int Category1 { get; set; }
[DataMember(IsRequired = true, Order = 2)]
public int Category2 { get; set; }
[DataMember(IsRequired = true, Order = 3)]
public int Category3 { get; set; }
[DataMember(IsRequired = true, Order = 4)]
public int Category4 { get; set; }
}
And the service interface is simple:
public interface IQuizService
{
[OperationContract]
[WebInvoke(Method = "POST",
BodyStyle = WebMessageBodyStyle.WrappedRequest,
ResponseFormat = WebMessageFormat.Json)]
ServiceResult Save(QuizServiceArgs answers, string strvalue, int intvalue);
}
The second two params strvalue and intvalue were added only for troubleshooting to see if those were getting deserialized -- and they are. When I hit the service, I get an error saying that I'm missing the Category1 parameter from the request but as you can see this Fiddler screenshot, the values are there.
I can get primitive values to pass in but objects seem to all be instantiated with null or default values. What am I doing wrong?
UPDATE
I never actually got my original question answered which sucks, but Sixto suggested that I switch my serialization to JSON. JSON was the original design but got nixed when I was having trouble with it. After I successfully switched back to JSON, everything was serializing and deserializing properly. Now I am just waiting for this to break for no explanation so I can switch back to XML....
You have a namespace issue. By default, when you create an interface for the servicecontract, it assigns it a namespace. Namespace is like scope for the SOAP xml elements, and if it doesn't fall under the same scope, it thinks it doesn't exist. Chances are the code that posts stoppped providing the namespace(?). You have to refer by it when posting the XML - but I'm not entirely sure what it assigns(something server specific), so it is good practice to always define a namespace, like so:
[ServiceContract(Namespace = "your namespace")]
public interface IQuizService
{
[OperationContract]
[WebInvoke(Method = "POST",
BodyStyle = WebMessageBodyStyle.WrappedRequest,
ResponseFormat = WebMessageFormat.Json)]
ServiceResult Save(QuizServiceArgs answers, string strvalue,
int intvalue);
}
And then the posts should have the namespace in the SOAP request.
<Save xmlns="your namespace">.....</Save>
The namespace should also match your service declaration in web.config.
Also need it in the datacontract
[DataContract(Namespace = "your namespace")]
The DataMember IsRequired attribute tells the WCF deserializer to expected an element in the message XML with that name (at least it does for soap deserialization). It sounds like the client code that is generating the message request is no longer sending all the Category... elements. It doesn't mean you can't send a null value for a DataMember marked IsRequired, it just means the element needs to exist in the message.
UPDATE: looking at the XML more closely, shouldn't there be a QuizServiceArgs element in the XML?

How do I return XML in a RESTful .NET WCF Web Service?

I set up a WCF Web Service in Visual Web Developer 2010 Express using the 4.0 Framework and converted it to a RESTful service using this tutorial
I was able to modify it to my liking to accept url parameters like so:
namespace RestServicePublishing
{
[ServiceContract]
public interface IRestService
{
[OperationContract(Name="GetXML")]
[WebGet(UriTemplate = "/{param1}/{param2}")]
XmlDocument GetXML(string param1, string param2);
}
}
The issue I am having is that I am getting a "Type 'System.Xml.XmlDocument' cannot be serialized" error when trying to return an XML document like this:
namespace RestServicePublishing
{
public class RestService : IRestService
{
public XmlDocument GetXML(string param1, string param2)
{
//I am not using the parameters currently, I would just like to see if
//i can return XML first with this simple example:
StringBuilder sb = new StringBuilder();
System.Xml.XmlWriter writer = XmlWriter.Create(sb);
writer.WriteStartDocument();
writer.WriteStartElement("People");
writer.WriteStartElement("Person");
writer.WriteAttributeString("Name", "Nick");
writer.WriteEndElement();
writer.WriteStartElement("Person");
writer.WriteStartAttribute("Name");
writer.WriteValue("Nick");
writer.WriteEndAttribute();
writer.WriteEndElement();
writer.WriteEndElement();
writer.WriteEndDocument();
writer.Flush();
XmlDocument xmlDocument = new Xml.XmlDocument();
xmlDocument.LoadXml(sb.ToString());
return xmlDocument;
}
}
}
I know there has to be a better way to set up an XML document and return it.. Any help is greatly appreciated!
Thank you in advance!!
Yes - well.... the model for WCF says that you should not try to return an XmlDocument itself. Instead you return a custom type defined inside your programming environment. That type needs to be marked up to specify how it should be serialized into XML. Then when that method returns the custom type, WCF serializes it into an XML document implicitly.
I think what you want to return is something like this:
<People>
<Person Name="Nick"/>
<Person Name="Bonnie"/>
</People>
But the DataContractSerializer doesn't like to emit attributes. So using WCF in the normal way to produce XML web services, you will instead get something like this:
<People>
<Person><Name>Nick</Name></Person>
<Person><Name>Bonnie</Name></Person>
</People>
To do that, write your C# code like this:
namespace RestServicePublishing
{
[ServiceContract]
public interface IRestService
{
[OperationContract(Name="GetXML")]
[WebGet(UriTemplate = "/{param1}/{param2}")]
List<Person> GetXML(string param1, string param2);
}
}
Then the type ought to look like this:
[DataContract]
public class Person
{
[DataMember]
public string Name { get; set; }
}
[CollectionDataContract(Name = "People")]
public class People : List<Person>
{
}
Return it as a string and then load that string into an XmlDocument at the other end.
Is there an issue sending XML via WCF?
Or preferably, create a DataContract class that mimics the XML structure in code, and then WCF will turn it into XML for you.

Categories