c# Passing array of objects with global namespace to WebMethod - c#

I am not being able to generate the request for the following xml:
<soapenv:Envelope
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:sga="urn:erp-ws-schemas/codeunit/SGA"
xmlns:rec="RecogerAlbaranes">
<soapenv:Header/>
<soapenv:Body>
<sga:RecogerAlbaranes>
<sga:parametros>
<!--1 or more repetitions:-->
<Fila>
<id>0001</id>
....
</Fila>
<Fila>
<id>0002</id>
....
</Fila>
.....
</sga:parametros>
</sga:RecogerAlbaranes>
</soapenv:Body>
</soapenv:Envelope>
The xml request that my webservice has is this one.
The problem is that Fila is using the wrong namespace.
<soapenv:Envelope
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:sga="urn:erp-ws-schemas/codeunit/SGA"
xmlns:rec="RecogerAlbaranes">
<soapenv:Header/>
<soapenv:Body>
<sga:RecogerAlbaranes>
<sga:parametros>
<!--1 or more repetitions:-->
<sga:Fila>
<id></id>
...
</sga:Fila>
....
</sga:parametros>
</sga:RecogerAlbaranes>
</soapenv:Body>
</soapenv:Envelope>
My webmethod is like this
[WebMethod]
[SoapDocumentMethod(Action = "RecogerAlbaranes")]
[return: XmlElementAttribute("response")]
public XmlDocument RecogerAlbaranes(List<Fila> parametros)
{ ... }
The class Fila:
<XmlRoot([Namespace]:="">
Public Class Fila
{ ... }
I tried to using the attribute xmlns inside Fila class but gives me an error becase its not allowed.
I also tried wrapping Fila array inside a class named cParameters, but that generates a nested <parametros>
<XmlRoot([Namespace]:="">
Public Class cParametros
{
[XmlArrayItem(ElementName = "Fila", Type = typeof(Fila))]
[XmlArray(ElementName = "parametros")]
public List<Fila> parametros { get; set; } = new List<Fila>();
}
public XmlDocument RecogerAlbaranes(cParametros parametros)
{ ... }
I have also tried some parameters of [SoapDocumentMethod] but without success.
I'm starting to get desperate, is this even possible?

Related

Using multiple namespaces in WCF

I have an application in C# Then i have to give a requested result as below.
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<s:Body>
<MyAppResponse xmlns="http://www.specificAddress.com">
<MyAppResult xmlns:abc1="http://schemas.specificAddress.Responses" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<abc1:ResponseData xmlns:aef2="http://schemas.datacontract.org/Test.GenericMerchantServices.Classes.Domain">
<aef2:ResponseCode>0000</aef2:ResponseCode>
<aef2:ResponseDescription>Successful</aef2:ResponseDescription>
</abc1:ResponseData>
</MyAppResult>
</MyAppResponse>
</s:Body>
</s:Envelope>
In this case i am using a .NET 5 app and soapCore to work with soap in .NET Core. Here ise my interface and derived class.
[ServiceContract]
public interface IMyAppService
{
[OperationContract]
MyAppResult MyApp(MyAppRequest request);
}
public class MyAppService : IMyAppService
{
public MyAppResult MyApp(MyAppRequest request)
{
return new MyAppResult()
{
ResponseData = new ResponseData()
{
ResponseCode = "0000",
ResponseDescription = "Test "
}
};
}
}
I've tried to update namespaces with data annotiations but i don't know how can i update prefix and make response as requested. Here is my class
[XmlRoot(Namespace = "http://schemas.specificAddress.Responses")]
public class MyAppResult
{
[XmlElement(ElementName = "ResponseData", Namespace = "http://schemas.datacontract.org/Test.GenericMerchantServices.Classes.Domain")]
public ResponseData ResponseData { get; set; }
}
public class ResponseData
{
public string ResponseCode { get; set; }
public string ResponseDescription { get; set; }
}
When i use this endpoint i get this result.
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<s:Body>
<MyAppResponse xmlns="http://tempuri.org/">
<MyAppResult xmlns="http://schemas.specificAddress.Responses" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<ResponseData xmlns="http://schemas.datacontract.org/Test.GenericMerchantServices.Classes.Domain">
<ResponseCode>0000</ResponseCode>
<ResponseDescription>Test </ResponseDescription>
</ResponseData>
</MyAppResult>
</MyAppResponse>
</s:Body>
</s:Envelope>
The question is how can i add ns and custom prefix to my project and how can i add custom namespace on myappresponse.
To change the namespace of a WCF service, you need to apply the Namespace attribute to the ServiceContractAttribute on the service contract interface. Details can be found at http://blog.rebuildall.net/2010/11/10/WCF_service_namespaces
About adding prefixes you can take a look at this document.
XML Serialization and namespace prefixes
C# WCF Global Namespaces - Royal Mail

c# XmlSerializer List of typed objects

I have the problem that I got xml from a REST service and have to deserialize it to an object structure and there is a type="proglang" in it.
<listResult>
<listEntry xsi:type="proglang">
<id>0</id>
<name>C#</name>
</listEntry>
<listEntry xsi:type="proglang">
<id>0</id>
<name>C#</name>
</listEntry>
</listResult>
How should the object model with the xml attributes look like in c#?
My Code so far:
[XmlRoot("listResult")]
public class ListResult
{
[XmlElement("listEntry")]
//[XmlArrayItem(Type=typeof(proglang))]
public List<proglang> listEntry;
}
public class proglang
{
[XmlElement("code")]
public int id;
[XmlElement("label")]
public string name;
}
and the resulting XML from serializing:
<listResult xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<listEntry>
<code>1</code>
<label>C#</label>
</listEntry>
<listEntry>
<code>1</code>
<label>C++</label>
</listEntry>
</listResult>

Deserialize using DataContractSerializer

I am trying to Deserialize an xml file that looks like the following
<?xml version="1.0"?>
<Test xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/ConsoleApplication6">
<values>
<String>Value 1</String>
<String>Value 2</String>
</values>
</Test>
to an object that is this
[DataContract(Namespace = "http://schemas.datacontract.org/2004/07/ConsoleApplication6")]
public class Test
{
[DataMember(Name = "values")]
public String[] values;
}
with
var ds = new DataContractSerializer(typeof(Test));
using (Stream stream1 = File.OpenRead(#"C:\Projects\test1.xml"))
{
Test rr = (Test)ds.ReadObject(stream1);
}
However none of the values are deserializing. I just see and empty array in Test rr. Could you please tell what I am doing wrong. Thanks in advance.
If you need fine control of the XML that is emitted when serializing, you should not use DataContractSerializer. It is has very limited flexibility. You would be better off using XmlSerializer, which has liimtitations as well, but is much more flexible than DataContractSerializer.
That being said, here is how you can do what you want with DataContractSerializer.
Change the default namespace on your xml to use the one that DataContractSerializeruses by default.
<?xml version="1.0"?>
<Test xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/">
<values>
<String>Value 1</String>
<String>Value 2</String>
</values>
</Test>
Instead of using string[] create your own custom type that derives from List<string>. This must be done solely for the purpose of having something to hang CollectionDataContractAttribute on. CollectionDataContractAttribute is what will let you specify the name of the elements inside <values>.
[DataContract]
public class Test
{
[DataMember(Name = "values")]
public TestValues values;
}
[CollectionDataContract(ItemName = "String")]
public class TestValues : List<string> { }
The DataContractSerializer has its own rules for XML and cannot support all XML forms. I suggest using the XmlSerializer.
Use this definition
[XmlClass(Namespace = "http://schemas.datacontract.org/2004/07/ConsoleApplication6")]
public class Test
{
[XmlArray("values")]
[XmlArrayItem("String")]
public String[] values;
}

Change REST Method Response XML Node name in MVC 4

We are building a REST Service using MVC4 and added method like:
public IEnumerable<Contact> GetAllContacts()
{
return repository.GetAll();
}
The response of this method in XML generated as:
<ArrayOfContact xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Contact>
<Id>1</Id>
<Name>Alice</Name>
<Email>alice#example.com</Email>
</Contact>
<Contact>
<Id>2</Id>
<Name>Bob</Name>
<Email>bob#example.com</Email>
</Contact>
<Contact>
<Id>3</Id>
<Name>Charles</Name>
<Email>charles#example.com</Email>
</Contact>
</ArrayOfContact>
I would like to change the tag <ArrayOfContact> to <Contacts>.
I know if it would be a property of a class then we could add attributes to the property to change it.
But is it possible to change the Root Node of Response?
It's easy to fix it with the creation of a classe Contacts.
[DataContract(Namespace = "a namespace",Name="Contacts")]
public class Contacts
{
private List<Contact> contacts= null;
[DataMember]
public List<Contact> Contacts
{
get
{
if (contacts == null)
{
contacts = new List<Contact>();
}
return contacts;
}
set
{
contacts= value;
}
}
}
And then you will get an XML with the wanted header.

Can I control the XML element names for an array and its items as the return value from an asynchronous .Net ASMX web method?

Consider the following .Net ASMX web service with two web methods.
using System;
using System.Runtime.Remoting.Messaging;
using System.Web.Services;
using System.Xml.Serialization;
namespace DemoWebService
{
public class
ArrayItem
{
public int
Value1;
public int
Value2;
};
[WebService(Namespace = "http://begen.name/xml/namespace/2009/09/demo-web-service/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class DemoService :
WebService
{
[WebMethod]
[return: XmlArray("Items")]
[return: XmlArrayItem("Item")]
public ArrayItem []
GetArray()
{
return BuildArray();
}
[WebMethod]
public IAsyncResult
BeginGetArrayAsync(AsyncCallback callback, object callbackData)
{
BuildArrayDelegate fn = BuildArray;
return fn.BeginInvoke(callback, callbackData);
}
[WebMethod]
[return: XmlArray("Items")]
[return: XmlArrayItem("Item")]
public ArrayItem []
EndGetArrayAsync(IAsyncResult result)
{
BuildArrayDelegate fn = (BuildArrayDelegate)((AsyncResult)result).AsyncDelegate;
return fn.EndInvoke(result);
}
private delegate ArrayItem []
BuildArrayDelegate();
private ArrayItem []
BuildArray()
{
ArrayItem [] retval = new ArrayItem[2];
retval[0] = new ArrayItem();
retval[0].Value1 = 1;
retval[0].Value2 = 2;
retval[1] = new ArrayItem();
retval[1].Value1 = 3;
retval[1].Value2 = 4;
return retval;
}
}
}
The GetArray and GetArrayAsync web methods both return an array of ArrayItems. The GetArrayAsync web method, however, is implemented using the asynchronous programming model.
Through the use of the [XmlArray] and [XmlArrayItem] attributes applied to the return value of the GetArray() method, I've been able to control the XML elements .Net uses to serialize the return value and get a result like this:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<GetArrayResponse xmlns="http://begen.name/xml/namespace/2009/09/demo-web-service/">
<Items>
<Item>
<Value1>int</Value1>
<Value2>int</Value2>
</Item>
<Item>
<Value1>int</Value1>
<Value2>int</Value2>
</Item>
</Items>
</GetArrayResponse>
</soap:Body>
</soap:Envelope>
Notice how the return value has the XML element name of "Items" and each item in the array has the XML element name of "Item".
I have applied the same attributes to the EndGetArrayAsync() method, but they don't seem to affect the response the way they do for the synchronous GetArray() method. In this case the response looks like this:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<GetArrayAsyncResponse xmlns="http://begen.name/xml/namespace/2009/09/demo-web-service/">
<GetArrayAsyncResult>
<ArrayItem>
<Value1>int</Value1>
<Value2>int</Value2>
</ArrayItem>
<ArrayItem>
<Value1>int</Value1>
<Value2>int</Value2>
</ArrayItem>
</GetArrayAsyncResult>
</GetArrayAsyncResponse>
</soap:Body>
</soap:Envelope>
In this case, the return value was given the default XML element name of "GetArrayAsyncResult" and the array items have the XML element name coming from the C# class name, "ArrayItem".
Is there any way to get an asynchronous web method to respect the [XmlArray] and [XmlArrayItem] attributes applied to the return value of the End* method?
See this question...
You need to add the XmlType() attribute to the class used in the array and then specify only the XmlArray() attribute on the class member defining the array.
I believe this to be a bug in the .Net framework as the only difference between the two web methods in my example is that one is asynchronous and the two [return: *] attributes are placed on the End*() method of the asynchronous implementation.
I have found a work-around that makes the WSDL essentially identical for the synchronous and asynchronous versions of the web method, and thus makes the SOAP response use the XML element names I want for the array.
I need to change the End*() method to not have a return value and instead use an output parameter to return the array. The [XmlArrayItem] attribute is then placed on that parameter instead of the return value of the method. Here is the revised EndGetArrayAsync() method that does what I want.
[WebMethod]
public void
EndGetArrayAsync(IAsyncResult result, [XmlArrayItem("Item")] out ArrayItem [] Items)
{
BuildArrayDelegate fn = (BuildArrayDelegate)((AsyncResult)result).AsyncDelegate;
Items = fn.EndInvoke(result);
}

Categories

Resources