WSDL time format is ignored from Visual Studio - c#

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{...

Related

C# code first grpc : custom protobuf converter for DateTime

I'm using something like this in dotnet asp net core 6:
<PackageReference Include="protobuf-net.Grpc.AspNetCore" Version="1.0.152" />
<PackageReference Include="protobuf-net.Grpc.AspNetCore.Reflection" Version="1.0.152" />
[DataContract]
public class TaskItem
{
//other properties omitted
[DataMember(Order = 5)]
public DateTime DueDate { get; set; } = null!;
}
Now, when I call the service with grpcurl
"DueDate": {
"value": "458398",
"scale": "HOURS"
}
And in the generated proto file
import "protobuf-net/bcl.proto"; // schema for protobuf-net's handling of core .NET types
message TaskItem {
//other properties omitted
.bcl.DateTime DueDate = 5;
Is there a way to specify a custom converter so that it will serialize to ISO 8601 string in order to better support cross platform (I'll have some clients in js where having a string is ok since I just need new Date(v) and d.toISOString()) ?
I know I can just declare DueDate as string, but then the "problem" is that when I use C# code-first client I also need to convert back to DateTime and to string ...
For example, I can do the following with JSON
.AddJsonOptions(x =>
{
x.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
});
What you ask is very different from a JSON type converter. As the docs explain the standard way of serializing dates is the google.protobuf.Timestamp type. That's defined in the proto file. When you use code-first that file is generated by the open source protobuf-net.Grpc tool.
To use the Timestamp type you need to tell the tool to format that property using a well-known type with the ProtoMember attribute :
[ProtoMember(1, DataFormat = DataFormat.WellKnown)]
public DateTime Time { get; set; }
This is shown in the tool's Getting Started document.
This isn't the default for legacy reasons :
(for legacy reasons, protobuf-net defaults to a different library-specific layout that pre-dates the introduction of .google.protobuf.Timestamp). It is recommended to use DataFormat.WellKnown on DateTime and TimeSpan values whenever possible.

Response structure for web service in C#

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.

Overriding a Generated Partial Class

I have imported a third-party WSDL (via Service Reference) into my Console Application project in order to send and receive data through Web Services. In order to get this to function appropriately, I have had to add some code to the Reference.cs file associated to the Web Service. While this works, if an update is made to the WSDL, and I re-import/generate that Service Reference, that work-around code will go away.
In order to get around this, I have saved the necessary code-changes to an external text file saved within the project.
I'm curious if anyone knows of a way that I could write these changes into a their own separate class, outside of the Service Reference, and yet, still be referenced by the Service Reference, thus using the "correct" code needed to send/receive from the Web Service.
I have two classes (not included in the generated code) that I am able to reference in the generated code after separating them into their own .cs file and referencing the namespace used by the Service Reference.
What I would like to do, if possible, is the following:
Overall Goal:
Add custom code to code generated by importing a third-party WSDL as a Service Reference, that way when the WSDL is updated by the third-party, another developer would not have to necessarily remember to dive into the Reference.cs file of the Service Reference, and replace/add specific code.
To achieve this goal, I need to be able to:
Replace an existing property and associated field of the generated
partial class, with a customized version (see Snippet #1 below).
Replace an existing generated partial class with a customized version of the class, having a different attribute definition and slightly different property/field definitions.
Snippet #1
Replace the following:
private byte[] bulkExchangeFileField;
[System.Xml.Serialization.XmlElementAttribute(Namespace = "urn:us:gov:treasury:irs:common", DataType = "base64Binary", Order = 0)]
public byte[] BulkExchangeFile
{
get { return this.bulkExchangeFileField; }
set
{
this.bulkExchangeFileField = value;
this.RaisePropertyChanged("BulkExchangeFile");
}
}
with this version of the properties/fields that worked once I altered the generated code:
private BulkExchangeFileType bulkExchangeFileField;
[System.Xml.Serialization.XmlElementAttribute(Namespace = "urn:us:gov:treasury:irs:common", Order = 0)]
public BulkExchangeFileType BulkExchangeFile
{
get { return this.bulkExchangeFileField; }
set
{
this.bulkExchangeFileField = value;
this.RaisePropertyChanged("BulkExchangeFile");
}
}
Use extension methods and/or overload the properties in an inhered class, so your code will not be replaced.
To overload the properties you just need to declare it with the word new before public like in : new public BulkExchangeFileType BulkExchangeFile, so when you use the object it will call your properties instead the ones defined by the web service
and here is how to create extention methods https://msdn.microsoft.com/library/bb383977.aspx
class Program
{
static void Main(string[] args)
{
InheredClass test = new InheredClass(); // Do this
BaseClass test2 = new InheredClass(); // don't do this
Console.WriteLine(test.MyProperty.GetType());
Console.WriteLine(test2.MyProperty.GetType());
Console.Read();
}
class BaseClass
{
public int MyProperty { get; set; }
}
class InheredClass : BaseClass
{
new public decimal MyProperty { get; set; }
}
}

Validating inherited attribute using DataAnnotations

I have MVC project that relies on webservices to provide data and those webservices are based on CMIS specification with custom functionality. I have several classes used as DataContracts, which were created by Visual Studio when I added references to services I am calling. I am using that class as a model to ensure I am able to send instances to the service and process correctly those sent back to me.
I also have views to edit instances of those classes and I would like to use DataAnnotations to validate the forms (usually [Required] atribute and sometimes display name change).
I do not want to put those atributes in service reference files because updating the reference would mean I will loose those atributes (at least I could not be sure everything is still the same after reference update).
My thought was to create child class that would only serve as tool to introduce DataAnnotations to atributes I know for sure I will be using (those that will not dissapear from DataContract class for sure). How would I accomplish such inheritance with code?
Example - I have this class created by VS in reference.cs file:
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "4.0.0.0")]
[System.Runtime.Serialization.DataContractAttribute(Name="LibraryRequest", Namespace="http://schemas.datacontract.org/2004/07/Agamemnon.Models")]
[System.SerializableAttribute()]
public partial class LibraryRequest : DocuLive.RepositoryServiceExt.Library {
[System.Runtime.Serialization.OptionalFieldAttribute()]
private string PasswordField;
[System.Runtime.Serialization.OptionalFieldAttribute()]
private string ServerField;
[System.Runtime.Serialization.OptionalFieldAttribute()]
private bool UseDefaultField;
[System.Runtime.Serialization.OptionalFieldAttribute()]
private string UserNameField;
[System.Runtime.Serialization.DataMemberAttribute()]
public string Password {
get {
return this.PasswordField;
}
set {
if ((object.ReferenceEquals(this.PasswordField, value) != true)) {
this.PasswordField = value;
this.RaisePropertyChanged("Password");
}
}
}
[System.Runtime.Serialization.DataMemberAttribute()]
public string Server {
get {
return this.ServerField;
}
set {
if ((object.ReferenceEquals(this.ServerField, value) != true)) {
this.ServerField = value;
this.RaisePropertyChanged("Server");
}
}
}
[System.Runtime.Serialization.DataMemberAttribute()]
public bool UseDefault {
get {
return this.UseDefaultField;
}
set {
if ((this.UseDefaultField.Equals(value) != true)) {
this.UseDefaultField = value;
this.RaisePropertyChanged("UseDefault");
}
}
}
[System.Runtime.Serialization.DataMemberAttribute()]
public string UserName {
get {
return this.UserNameField;
}
set {
if ((object.ReferenceEquals(this.UserNameField, value) != true)) {
this.UserNameField = value;
this.RaisePropertyChanged("UserName");
}
}
}
}
I want to make sure that no matter what changes in reference.cs file (even that class itself), I will always have Username, Password and Server marked as [Required] in my "Edit" and "Delete" forms.
Thanks in advance
Honza
I would stay away from inheriting an autogenerated class. It would not solve your problem with the attributes - you would have to override every single property so you can add attributes to it.
One solution is to use hand-coded datacontracts instead of autogenerated references. You will have full control over when they change, and you can put the attributes you need in them.
Another solution is wrapping the contract in your view model. Like this:
public class LibraryRequestViewModel {
private LibraryRequest request;
public LibraryRequestViewModel(LibraryRequest request){
this.request = request;
}
[Required]
public string Password {
get { return this.request.Password; }
set { this.request.Password = value; }
}
// do this for all fields you need
}

wcf - creating datacontract

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.

Categories