I'm trying to replicate an old soap service, which is in production at the moment. The contract, requests and responses has to be the exact same so that we dont need to update all dependent systems which is using this service. The thing with this old soap service, is that one of the responses are pretty weird. It has the following structure:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns="http://something" xmlns:ns1="http://something1">
<soap:Body>
<ns:MyResponse>
<ns:GetInfo>
<ns1:IdNumber>12345</ns:IdNumber>
<ns1:PersondataList>
<ns1:FirstName>John</ns1:FirstName>
<ns1:LastName>Travolta</ns1:LastName>
</ns1:PersondataList>
</ns:GetInfo>
</ns:MyResponse>
</soap:Body>
</soap:envelope>
This immiditaly makes me thinking of the following structure in code:
public class GetInfo
{
public string IdNumber {get; set;}
public PersonData[] PersondataList {get; set;}
}
public class PersonData
{
public string FirstName {get; set;}
public string LastName {get; set;}
}
When testing this in SoapUI, my response are the following:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns="http://something" xmlns:ns1="http://something1">
<soap:Body>
<ns:MyResponse>
<ns:GetInfo>
<ns1:IdNumber>12345</ns:IdNumber>
<ns1:PersondataList>
<ns1:Persondata>
<ns1:FirstName>John</ns1:FirstName>
<ns1:LastName>Travolta</ns1:LastName>
<ns1:Persondata>
</ns1:PersondataList>
</ns:GetInfo>
</ns:MyResponse>
</soap:Body>
</soap:envelope>
As you can see, the difference between the original soap response and my replication is the Persondata tag before FirstName and LastName. In my opinion this is the correct structure, but as mentioned before, I need to replicate the response in the exact same way...
How can I produce the same structure as the original response? Do I need to write my own Serializer? Is there any attributes that I can mark my properties with?
Thanks in advance.
For those of you stumbling upon this type of problems. Add the following attribute to your property:
[MessageBodyMember(Namespace = "Some namespace"), XmlElement]
The end result:
public class GetInfo
{
public string IdNumber {get; set;}
[MessageBodyMember(Namespace = "Some namespace"), XmlElement]
public PersonData[] PersondataList {get; set;}
}
Related
I have a very difficult formatted XML document , which I want to read it and use its parameters.
This is the XML that I want to deserialize:
<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<APIResponse xmlns="https://testwebservice.com/">
<Result>Api Result Message</Result>
</APIResponse>
</soap:Body>
</soap:Envelope>
I have tried some code samples that I found in some other question here
APIResponse envelopeClass = new APIResponse();
XmlSerializer serializer = new XmlSerializer(typeof(APIResponse), new XmlRootAttribute("Envelope"));
StringReader stringReader = new StringReader(xmlString);
envelopeClass = (APIResponse)serializer.Deserialize(stringReader);
But nothing has helped me so far , as I get an ERROR like this:
System.InvalidOperationException: 'There is an error in XML document (1, 42).'
Inner Exception
InvalidOperationException: <Envelope xmlns='http://schemas.xmlsoap.org/soap/envelope/'> was not expected.
These are the classes that I have used so far with the Paste Special button in Visual Studio.
[System.SerializableAttribute()]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://schemas.xmlsoap.org/soap/envelope/")]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "http://schemas.xmlsoap.org/soap/envelope/", IsNullable = false)]
public partial class Envelope
{
public EnvelopeBody bodyField { get; set; }
}
[System.SerializableAttribute()]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://schemas.xmlsoap.org/soap/envelope/")]
public partial class EnvelopeBody
{
[System.Xml.Serialization.XmlElementAttribute(Namespace = "https://testwebservice.com/")]
public APIResponse aPIResponseField { get; set; }
}
[System.SerializableAttribute()]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="https://testwebservice.com/")]
[System.Xml.Serialization.XmlRootAttribute(Namespace="https://testwebservice.com/", IsNullable=false)]
public partial class APIResponse
{
public string resultField { get; set; }
}
Despite all these I have not understand why I get the above ERROR , I want to note that I have not fully understand how all this work.
Anyway , if there is anyone that can help with this I would appreciate it.
Please show me which are the right classes that I should use with this XML format document and how to deserialize it.
The "Difficult XML Format" as you name it is actually a standard SOAP (Simple Object Access Protocol) message. SOAP is used as the communication protocol for XML based web services, which at first brings to mind that you may add a service reference for the web service from where you get this XML content. If you are getting this XML content from a web resource, please try to add it as a service reference.
If not and if you've just ended up with this XML content somehow, although there are examples of how to get the response object from inside the xml by parsing it and serializing only part of it, here is what I assume to be a more elaborate and safe way of achieving it.
Add NuGet Reference: Microsoft.Web.Services3
Change the class ApiResponse as follows (change the name resultField to Result)
[System.SerializableAttribute()]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "https://testwebservice.com/")]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "https://testwebservice.com/", IsNullable = false)]
public partial class APIResponse
{
public string Result { get; set; }
}
And then get the ApiResponse object using the SoapEnvelope class and its methods like:
using Microsoft.Web.Services3;
. . .
. . .
SoapEnvelope envelope = new SoapEnvelope();
envelope.LoadXml(xmlString);
APIResponse apiResponse = (APIResponse)envelope.GetBodyObject(typeof(APIResponse));
. . .
. . .
I'm using DataContractSerializer in my Web API application and in my action I'm returning a Data Type as below:
public class Event
{
public string Name {get; set;}
public IList<Division> Divisions {get;set;}
}
When serialized it's returning the below xml:
<Event xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07
/EventTypeNameSpace">
<Name>some name</Name>
<Divisions i:nil="true" />
</Event>
1) Why is it returning two xmlns:i and xmlns attributes? how can they be excluded?
2) How can I exclude the Divisions from the xml when it's null?
1: the "http://schemas.datacontract.org/2004/07" is the default namespace used by types serialized by data-contract serializer; if you don't like that - change your contract; the "http://www.w3.org/2001/XMLSchema-instance" defines "nil" as a special value
2: by defining the contract properly
[DataContract(Namespace="")]
public class Event
{
[DataMember]
public string Name { get; set; }
[DataMember(EmitDefaultValue=false)]
public IList<Division> Divisions { get; set; }
}
However: I should add - if you want tight control over what the layout looks like, you should probably be using XmlSerializer, not DataContractSerializer
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>
My web api is returning a set of objects which are differ from the Domain object. Forexample, I my domain has an Employee class but I don't want to expose all the members of the Employee class in my api so I created another class called EmployeeApiModel.
Now my WebApi is returning a List of EmployeeApiModel but I want to be able to specify the name to which it should serialize to. That is instead of <EmployeeApiModel> tag in the xml, I want to get <Employee> but without changing the fact that the underlying class which is being serialized is EmployeeApiModel.
How can I achieve this?
Technically, Web Api support both json and xml based on content negotiation mechanism, Json is the default format, if you want to receive xml, just put on header:
Accept: application/xml
To understand more content negotiation, access this
Since you want your api support both json and xml, you should use DataContract and DataMember Attribute for serialization for your model: EmployeeApiModel, something like:
[DataContract(Name = "Employee")]
public class EmployeeApiModel
{
[DataMember(Name = "Name2")]
public string Name { get; set; }
[DataMember]
public string Email { get; set; }
}
See more on this blog-post
You can control the output of your serialized XML by using various Attribute tags.
[XmlRoot("Employee")]
Public class EmployeeApiModel
{
[XmlElement("fname")]
public string FirstName { get; set; }
public string LastName { get; set; }
public int age { get; set; }
}
this will produce XML like:
<Employee>
<fname>John</fname>
<LastName >Smith</LastName >
<age>24</age>
</RootElementsName>
You can read more about the various XML modifiers here: http://msdn.microsoft.com/en-us/library/e123c76w.
If you want to use existing XML modifiers for JSON, check out this post: Serialize .Net object to json, controlled using xml attributes
I have a WCF REST Starter Kit service. The type handled by the service is a subclass of a base class. For POST requests, the base class members are not correctly populated.
The class hierarchy looks like this:
[DataContract]
public class BaseTreeItem
{
[DataMember]
public String Id { get; set; }
[DataMember]
public String Description { get; set; }
}
[DataContract]
public class Discipline : BaseTreeItem
{
...
}
The service definition looks like:
[WebHelp(Comment = "Retrieve a Discipline")]
[WebGet(UriTemplate = "discipline?id={id}")]
[OperationContract]
public Discipline getDiscipline(String id)
{
...
}
[WebHelp(Comment = "Create/Update/Delete a Discipline")]
[OperationContract]
[WebInvoke(Method = "POST", UriTemplate = "discipline")]
public WCF_Result DisciplineMaintenance(Discipline discipline)
{
...
}
Problem: While the GET works fine (returns the base class Id and Description), the POST does not populate Id and Description even though the XML contains the fields.
Sample XML:
<?xml version="1.0" encoding="utf-8"?>
<Discipline xmlns="http://schemas.datacontract.org/2004/07/xxx.yyy.zzz">
<DeleteFlag>7</DeleteFlag>
<Description>2</Description>
<Id>5</Id>
<DisciplineName>1</DisciplineName>
<DisciplineOwnerId>4</DisciplineOwnerId>
<DisciplineOwnerLoginName>3</DisciplineOwnerLoginName>
</Discipline>
Thanks for any assistance.
I could not solve the problem using a DataContractSerializer. I switched to using the XMLSerializerFormat and everything worked fine. In fact, the capabilities of the XMLSerializer are so much better that for purely XML work, it is probably better to use the XMLSerializer in all cases.