I have my class structure for response as below:
/// <remarks/>
public System.Collections.Generic.List<PaymentMethods> DisallowedPaymentMethods
{
get
{
return this.disallowedPaymentMethodsField;
}
set
{
this.disallowedPaymentMethodsField = value;
}
}
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.0.30319.18408")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace = "http://blcorp.net/PaymentInfoInquiryService")]
public partial class PaymentMethods
{
private string paymentMethodField;
/// <remarks/>
public string PaymentMethod
{
get
{
return this.paymentMethodField;
}
set
{
this.paymentMethodField = value;
}
}
}
It's creating response as below
<DisallowedPaymentMethods>
<PaymentMethods>
<PaymentMethod>CreditCard</PaymentMethod>
</PaymentMethods>
<PaymentMethods>
<PaymentMethod>OnlineCheck</PaymentMethod>
</PaymentMethods>
</DisallowedPaymentMethods>
but I want response to be shown as below
<DisallowedPaymentMethods>
<PaymentMethod>CreditCard</PaymentMethod>
<PaymentMethod>OnlineCheck</PaymentMethod>
</DisallowedPaymentMethods>
How to create my response class to generate appropriate response structure.
If you're using Visual Studio, the easiest way to get a start is to copy the response as you want it into the clipboard, then use the "Paste XML as Classes" feature under the Edit => Paste Special menu.
Generate Class From JSON or XML in Visual Studio from C-Sharp Corner
Generating Data Type Classes from XML from Microsoft Documentation
If you're not using Visual Studio, you can also try Xml2Csharp.com
Try setting your return type to this and populating as appropriate:
public System.Collections.Generic.List<PaymentMethod> DisallowedPaymentMethods
Your class structure, without decorations indicating otherwise, dictates what your serialized response is. I think you class structure is a bit off.
Related
I have to call a soap service from a C# .net core app and get the results in a class that I can use to do some logic. I did the soap request and the remote call works fine, but now I have to deserialize the xml soap response into a class. Here's an example of the response:
<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:tns="http://some_domain/soap/ApplicationServices">
<SOAP-ENV:Body>
<ns1:preparelabelsResponse xmlns:ns1="http://some_domain/soap/ApplicationServices">
<return xsi:type="SOAP-ENC:Array" SOAP-ENC:arrayType="tns:PrepareReturn[1]">
<item xsi:type="tns:PrepareReturn">
<clientref xsi:type="xsd:string">2015/0418/001</clientref>
<pclid xsi:type="xsd:string"></pclid>
<error xsi:type="xsd:string">Invalid timestamp 20191018105727</error>
</item>
</return>
</ns1:preparelabelsResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Since I'm using Visual Studio 2017 I tried adding a connected service in my .netcore app using the WCF Web Service Reference Provider and then call the method. The call failed with the message: Wrong XML format, that was probably generated by the server. Doing the same steps in a .net framework app worked fine though, and I got the proper response. So I suspect something is different in the .netcore wcf provider.
My second approach was to create the soap request manually and then parse the soap response. For constructing the request I have an elegant solution, but for parsing the response I don't. I tried to use the classes that were generated by the wcf provider, but the xml deserialization didn't work. I then tried to change the attributes to get it right, but didn't help. I added below those classes:
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("dotnet-svcutil", "1.0.0.1")]
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
[System.ServiceModel.MessageContractAttribute(WrapperName="preparelabelsResponse", WrapperNamespace="encoded", IsWrapped=true)]
public partial class preparelabelsResponse
{
[System.ServiceModel.MessageBodyMemberAttribute(Namespace="", Order=0)]
public PrepareReturn[] #return;
public preparelabelsResponse()
{
}
public preparelabelsResponse(PrepareReturn[] #return)
{
this.#return = #return;
}
}
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("dotnet-svcutil", "1.0.0.1")]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.Xml.Serialization.SoapTypeAttribute(Namespace="http://some_domain/soap/ApplicationServices")]
public partial class PrepareReturn
{
private string clientrefField;
private string pclidField;
private string errorField;
/// <remarks/>
public string clientref
{
get
{
return this.clientrefField;
}
set
{
this.clientrefField = value;
}
}
/// <remarks/>
public string pclid
{
get
{
return this.pclidField;
}
set
{
this.pclidField = value;
}
}
/// <remarks/>
public string error
{
get
{
return this.errorField;
}
set
{
this.errorField = value;
}
}
}
And the xml deserialization:
var soapResponse = XDocument.Load(sr);
XNamespace myns = "http://some_domain/soap/ApplicationServices";
var xml = soapResponse.Descendants(myns + "preparelabelsResponse").FirstOrDefault().ToString();
var result = Deserialize<preparelabelsResponse>(xml);
...
public static T Deserialize<T>(string xmlStr)
{
var serializer = new XmlSerializer(typeof(T));
T result;
using (TextReader reader = new StringReader(xmlStr))
{
result = (T)serializer.Deserialize(reader);
}
return result;
}
So what I could do is to strip the xml away of all the namespaces and attributes and deserialize it to a simple class, but that is not an elegant solution for my problem. What I want is to be able to create/decorate my classes in such a way that the deserialization will work without any altering of the actual xml contents.
A WSDL file from a customer specifies the time data type using this syntax: <xsd:simpleType name="time"><xsd:restriction base="xsd:time"><xsd:pattern value="[0-9]{2}:[0-9]{2}:[0-9]{2}"/></xsd:restriction></xsd:simpleType>
I included the WSDL file as "Web Reference" (not Service Reference) in a Visual Studio C# project. Which generates this code:
[System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, DataType="time")]
public System.DateTime I_TIMETO {
get {
return this.i_TIMETOField;
}
set {
this.i_TIMETOField = value;
}
}
The problem is that in the generated payload, the pattern from the WSDL file ([0-9]{2}:[0-9]{2}:[0-9]{2}), is completly ignored.
I.e. the payload looks like:
<I_TIMETO xmlns="">17:11:00.0000000+01:00</I_TIMETO>
instead of:
<I_TIMETO xmlns="">17:11:00</I_TIMETO>
It is not possible to change the Webservice and I don't want to change the auto generated code.
I think there is no good solution, so you have to edit the auto generated code.
Create a partial class of the auto generated code and add a string property with the correct formatting in it:
[System.Xml.Serialization.XmlElementAttribute(Form = System.Xml.Schema.XmlSchemaForm.Unqualified,
DataType = "string", ElementName = "I_TIMETO")]
public string I_TIMETO_STR
{
get
{
return this.i_TIMETOField.ToString("HH:mm:ss");
}
set
{
this.i_TIMETOField = DateTime.ParseExact(value, "HH:mm:ss", CultureInfo.InvariantCulture);
}
}
Now go to the auto generated property and add a XmlIgnore:
[System.Xml.Serialization.XmlIgnore]
public System.DateTime I_TIMETO{...
I have created ASP.NET WEBAPI service. Service has Post method, which has custom class type parameter ( PublishRequest class, XSD generated class from xsd schema).
PublishRequest class is generated using xsd.exe. we are using XmlSerializer( not DataContractSerializer, formatters.XmlFormatter.UseXmlSerializer = true;)
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace = ..)]
[System.Xml.Serialization.XmlRootAttribute("PublishRequest", Namespace =..,
IsNullable = false)]
public partial class PublishRequest
{
......
public ContextInfo Context{get ...;set ...;}
}
[System.SerializableAttribute()]
[System.Xml.Serialization.XmlTypeAttribute(Namespace =..)]
[System.Xml.Serialization.XmlRootAttribute("Context", Namespace =..,
IsNullable = false)]
public partial class ContextInfo
{
[System.Xml.Serialization.XmlElementAttribute("Account", typeof(AccountInfo),
IsNullable = true)]
[System.Xml.Serialization.XmlElementAttribute("EmpID", typeof(string))]
[System.Xml.Serialization.XmlElementAttribute("Office", typeof(string),
DataType="nonNegativeInteger")]
[System.Xml.Serialization.XmlChoiceIdentifierAttribute("ItemElementName")]
public object Item
{
get
{
return this.itemField;
}
set
{
this.itemField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlIgnoreAttribute()]
public ItemChoiceType ItemElementName
{
get
{
return this.itemElementNameField;
}
set
{
this.itemElementNameField = value;
}
}
}
.....
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.17929")]
[System.SerializableAttribute()]
[System.Xml.Serialization.XmlTypeAttribute(Namespace =.., IncludeInSchema = false)]
public enum ItemChoiceType
{
/// <remarks/>
Account,
/// <remarks/>
EmPID,
/// <remarks/>
Office
}
................................
[HttpPost]
public PublishResponse Publish(PublishRequest request) {
....
}
For XML request type, PublishRequest is getting properly deserialized..with all properties populated as per XML.
but for JSON Request type, PublishRequest is not getting properly deserialized, all properties of request coming null.
I verified json, by calling
JsonConvert.DeserializeObject(sJSon, typeof(PublishRequest))
this is returning properly deserialized object, with all property set.
also
JsonConvert.DeserializeObject(sJSon, typeof(PublishRequest))
returns proper deserialized object.
I found that for JSONMediaFormatter calls following overload https://github.com/mono/aspnetwebstack/blob/master/src/System.Net.Http.Formatting/Formatting/JsonMediaTypeFormatter.cs
JsonConvert.DeserializeObject(sJSon, typeof(PublishRequest),
jsonFormatter.SerializerSettings)
I made same call in unit test to aboe overload method, which returns object with all properties null. So, it seems something in jsonFormatter.SerializerSettings is causing problem to deserializing.
I compared default value of JsonSerializerSettings, with jsonFormatter.SerializerSettings.
only property which look different was ContractResolver.
for default JsonSerializerSettings, ContractResolver is null. For jsonFormatter.SerializerSettings, it is JsonContractResolver object.
If I set ContractResolver to null in FormatterConfig.cs, json request is getting deserialzied properly.
jsonFormatter.SerializerSettings.ContractResolver = null;
Can any one please guide me, what JsonContractResolver is doing. which causing deserialize problem. what is way to solve this. will this workaround cause any other issue?
while googling for solution, i found this http://json.codeplex.com/discussions/351981
So, it seems problem is with SerializeableAttribute.
DefaultContractResolver has property IgnoreSerializableAttribute. which if set to true. JSON serialiser/derserialise will ignore SerializeableAttribute.
But JsonContractResolver , which is derived from DefaultContractResolver, has IgnoreSerializableAttribute to false. so if, IgnoreSerializableAttribute is set to true,json serilization/derserilization works proplery.
DefaultContractResolver cr = jsonFormatter.SerializerSettings.ContractResolver as DefaultContractResolver;
if (cr != null)
{
cr.IgnoreSerializableAttribute = true;
}
This working properly. & issue with json serilzation gone. hope some WebAPI team member reached this question..& guide if this ok? or it will cause any other problem...
Within my API I'm trying to document the different field descriptions, however none of attributes seem to work. I know this functionality is supposed to have been recently implemented within WebAPI 5.1 (running WebAPI.HelpPage 5.1.2).
ASP.Net Web API Help Pages: Document Model Data Annotations - Work Item 877
I'm trying to document both my response model:
And the individual fields/properties
I've tried a mixture of XML comments, DataMember and Display attributes but none seem to be picked up.
/// <summary>
/// blah blah blah
/// </summary>
[DataContract(Name = "Application")]
public class Application
{
/// <summary>
/// Please Display!
/// </summary>
[DataMember(Order = 0)]
[Display(Description="Please Display!")]
[StringLength(11, MinimumLength = 11)]
public string ApplicationId { get; set; }
Here is a sample from my Areas/HelpPage/App_Start/HelpPageConfig.cs
namespace WebAPI.Areas.HelpPage
{
#pragma warning disable 1591
/// <summary>
/// Use this class to customize the Help Page.
/// For example you can set a custom <see cref="System.Web.Http.Description.IDocumentationProvider"/> to supply the documentation
/// or you can provide the samples for the requests/responses.
/// </summary>
public static class HelpPageConfig
{
public static void Register(HttpConfiguration config)
{
// remove unwanted formatters
config.Formatters.Clear();
var jsonsettings = new JsonSerializerSettings() { DateParseHandling = DateParseHandling.None };
config.Formatters.Add(new JsonMediaTypeFormatter());
config.Formatters.Add(new XmlMediaTypeFormatter());
config.SetDocumentationProvider(new XmlDocumentationProvider(HttpContext.Current.Server.MapPath("~/bin/WebAPI.XML")));
// create sample objects
config.SetSampleObjects(new Dictionary<Type, object>
{
{ typeof(MyResponse), new MyResponse() {
Message = "Key d795677d-6477-494f-80c5-874b318cc020 is not recognised",
Code = ResponseCode.InvalidKey, Id = null }
}
});
//*** More Sample Requests ***
}
}
#pragma warning restore 1591
}
Update 10/06/2014: My class definitions are stored in a separate library. I've noticed a discrepancy here. The main API and class definition library were generating separate XML files.
API Project
Definition Project
I've attempted to rectify this by making the Definition write to the same XML project. However this doesn't work, and the class definition entries aren't added.
To have a content displayed in Description section you need to feel section of XML comments. If you would have your model class placed inside your webapi project - then this would be a solution. Your problem is that you need to read xml documentation form 2 xml files and one time and XmlDocumentationProvider does not support that. My suggestion is to create your own MultipleFilesXmlDocumentationProvider with a little effort like this:
public class MultipleFilesXmlDocumentationProvider : IDocumentationProvider
{
IEnumerable<XmlDocumentationProvider> xmlDocumentationProviders;
public MultipleFilesXmlDocumentationProvider(IEnumerable<string> documentPaths)
{
xmlDocumentationProviders = documentPaths.Select(path => new XmlDocumentationProvider(path));
}
public string GetDocumentation(HttpParameterDescriptor parameterDescriptor)
{
foreach(XmlDocumentationProvider provider in xmlDocumentationProviders)
{
string documentation = provider.GetDocumentation(parameterDescriptor);
if(documentation != null)
return documentation;
}
return null;
}
public string GetDocumentation(HttpActionDescriptor actionDescriptor)
{
foreach (XmlDocumentationProvider provider in xmlDocumentationProviders)
{
string documentation = provider.GetDocumentation(actionDescriptor);
if (documentation != null)
return documentation;
}
return null;
}
public string GetDocumentation(HttpControllerDescriptor controllerDescriptor)
{
foreach (XmlDocumentationProvider provider in xmlDocumentationProviders)
{
string documentation = provider.GetDocumentation(controllerDescriptor);
if (documentation != null)
return documentation;
}
return null;
}
public string GetResponseDocumentation(HttpActionDescriptor actionDescriptor)
{
foreach (XmlDocumentationProvider provider in xmlDocumentationProviders)
{
string documentation = provider.GetDocumentation(actionDescriptor);
if (documentation != null)
return documentation;
}
return null;
}
}
This will be just wrapper over XmlDocumentationProvider - it will work with a collection of XmlDocumentationProvider and looks for the first one that will provide desired documentation. Then you change your configuration in HelpPageConfig to use your MultipleFilesXmlDocumentationProvider:
config.SetDocumentationProvider(
new MultipleFilesXmlDocumentationProvider(
new string[] {
HttpContext.Current.Server.MapPath("~/bin/WebAPI.XML"),
HttpContext.Current.Server.MapPath("~/bin/EntityModel.Definitions.XML")
}
)
);
Of course take into account that for the configuration above both XML files should be within WebAPI project bin folder.
I've been tasked with writing a WCF service. (have not done this before.) I've received the xsd of the xml that I'll be receiveing and i'm trying to translate this into a datacontract. I require help though.
An example of a portion of the xml is:
<tfsChequeId xmlns="http://www.something.com/XMLSchemas/itrs/tfs/v1">
<dic numericCode="20010411199194813505"/>
</tfsChequeId>
What I've done so far is:
[DataContract]
public class TFSChequeDic
{
[DataMember]
public string dic { get; set; }
}
How do I specify the numericCode attribute?
Any help would be gratefully received.
Kind Regards,
Fiona
UPDATE
A number of XSD's were provided to me. these are quite complex. On generating the datacontracts using svcutil.exe a number of errors were generated..
All of the following form:
Error: There was a validation error in the schemas provided for code generation:
Source:
Line: 85 Column: 6
Validation Error: Type 'http://www.something.com/XMLSchemas/itrs/common/v1:Docu
mentIdentifierCode' is not declared, or is not a simple type.
The generated datacontract is as follows:
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "4.0.0.0")]
[System.Runtime.Serialization.DataContractAttribute(Name="TfsChequeId", Namespace="http://www.something.com/XMLSchemas/itrs/tfs/v1")]
public partial class TfsChequeId : object, System.Runtime.Serialization.IExtensibleDataObject
{
private System.Runtime.Serialization.ExtensionDataObject extensionDataField;
private www.something.com.XMLSchemas.itrs.tfs.v1.TfsChequeIdDic dicField;
public System.Runtime.Serialization.ExtensionDataObject ExtensionData
{
get
{
return this.extensionDataField;
}
set
{
this.extensionDataField = value;
}
}
[System.Runtime.Serialization.DataMemberAttribute(EmitDefaultValue=false)]
public www.something.com.XMLSchemas.itrs.tfs.v1.TfsChequeIdDic dic
{
get
{
return this.dicField;
}
set
{
this.dicField = value;
}
}
}
However I've no idea how to use this.. ie set numericCode?
Any ideas/hints/tips would be gratefully received.
Fiona
You don't need to do it by hand. SvcUtil will generate a client proxy for you if you feed it the WSDL. Or are you creating the service itself?
Use the xsd tool to create a class object from the xsd provided.