Parsing SOAP objects - c#

I'm trying to Deserialize a SOAP message back into an instance of a custom class and am having some formatting issues. The class is defined in a c# file that was generated from SvcUtil.exe.
Messages that I receive from the service are formatted in the following fashion, but when I try to serialize my own from an instance of the same class, they look different...
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope"
xmlns:SOAP-ENC="http://www.w3.org/2003/05/soap-encoding"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:sde1="http://www.somedomain.com/xml/SomeName/"
xmlns:sde2="http://www.somedomain.com/xml/2013/05/17/SomeName.xsd">
<SOAP-ENV:Body>
<sde2:notification_message>
<sde2:startup_notification
xml_version="http://www.somedomain.com/xml/2013/05/17/SomeName.xsd"
reboot_type="SOAP_REBOOT_POWERON"
customer_version="unsupported feature"
firmware_version="2.2.2.2"
ip_address="192.168.1.11"
osd_state="OSD_STATE_OK"
timestamp="1970-01-01T00:09:00.048895+00:00"
callerType_ID="SELF"
serverTask_ID="0"
notification_ID="19"
task_type="TASK_STARTUP"
customer_ID="SOMENAME0129"
mac_address="00:00:00:00:00:00">
</sde2:startup_notification>
</sde2:notification_message>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
My own attempt...
<SOAP-ENV:Envelope
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:clr="http://schemas.microsoft.com/soap/encoding/clr/1.0"
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<a1:StartupNotification id="ref-1"
xmlns:a1="http://schemas.microsoft.com/clr/assem/MSGReceive%2C%20Version%3D1.0.0.0%2C%20Culture%3Dneutral%2C%20PublicKeyToken%3Dnull">
<reboot_typeField>SOAP_REBOOT_POWERON</reboot_typeField>
<xml_versionField id="ref-3">http://www.somedomain.com/xml/2013/05/17/SomeName.xsd</xml_versionField>
<ExtendedNotificationBase_x002B_ip_addressField id="ref-4">192.168.1.11</ExtendedNotificationBase_x002B_ip_addressField>
<ExtendedNotificationBase_x002B_firmware_versionField id="ref-5">2.2.2.2</ExtendedNotificationBase_x002B_firmware_versionField>
<ExtendedNotificationBase_x002B_customer_versionField id="ref-6">unsupported feature</ExtendedNotificationBase_x002B_customer_versionField>
<NotificationWithOSDState_x002B_osd_stateField>OSD_STATE_OK</NotificationWithOSDState_x002B_osd_stateField>
<NotificationBase_x002B_mac_addressField id="ref-7">00:00:00:00:00:00</NotificationBase_x002B_mac_addressField>
<NotificationBase_x002B_customer_IDField id="ref-8">SOMENAME0129</NotificationBase_x002B_customer_IDField>
<NotificationBase_x002B_task_typeField>TASK_STARTUP</NotificationBase_x002B_task_typeField>
<NotificationBase_x002B_notification_IDField>19</NotificationBase_x002B_notification_IDField>
<NotificationBase_x002B_serverTask_IDField>0</NotificationBase_x002B_serverTask_IDField>
<NotificationBase_x002B_callerType_IDField id="ref-9">SELF</NotificationBase_x002B_callerType_IDField>
<NotificationBase_x002B_timestampField>2016-10-27T14:03:03.7532987-04:00</NotificationBase_x002B_timestampField>
</a1:StartupNotification>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Here's the code I'm using to create an instance of the class I'd like to be able to Serialize/Deserialize. Right now I'm just attempting to serialize my own and visually compare the results to what I'm receiving from the service.
StartupNotification sn = new StartupNotification();
sn.xml_version = "http://www.somedomain.com/xml/2013/05/17/SomeName.xsd";
sn.reboot_type = RebootType.SOAP_REBOOT_POWERON;
sn.customer_version = "unsupported feature";
sn.firmware_version = "2.2.2.2";
sn.ip_address = "192.168.1.11";
sn.osd_state = OSD_State.OSD_STATE_OK;
sn.timestamp = DateTime.Now;
sn.callerType_ID = "SELF";
sn.serverTask_ID = 0;
sn.notification_ID = 19;
sn.task_type = TaskType.TASK_STARTUP;
sn.customer_ID = "SOMENAME0129";
sn.mac_address = "00:00:00:00:00:00";
NotificationContainer nc = new NotificationContainer();
nc.Item = sn;
SoapFormatter sf = new SoapFormatter();
MemoryStream ms = new MemoryStream();
sf.Serialize(ms, nc);
string output = Encoding.UTF8.GetString(ms.GetBuffer());
return output;
It would be very helpful if someone could potentially help me diagnose some reasons why my serialized object looks different from the output from a service that is using the same classes. I'm just looking for some leads to look into or if I'm overlooking something simple. Any help is appreciated, thanks!
EDIT: Added requested information on StartupNotificiation. This class inherits from like 3 other layers up but they're all formatted the same way, just adding new properties.
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "2.0.50727.3038")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://www.somedomain.com/xml/2013/05/17/SomeName.xsd")]
public partial class StartupNotification : ExtendedNotificationBase {
private RebootType reboot_typeField;
private string xml_versionField;
/// <remarks/>
[System.Xml.Serialization.XmlAttributeAttribute()]
public RebootType reboot_type {
get {
return this.reboot_typeField;
}
set {
this.reboot_typeField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlAttributeAttribute()]
public string xml_version {
get {
return this.xml_versionField;
}
set {
this.xml_versionField = value;
}
}
}

The SoapFormatter is intended to support RPC calls in remoting scenarios where both ends of the channel use .NET. It will add some metadata about the assembly and the types to the output.
If you want to create a SOAP message manually, you can better use the XmlSerializer and wrap your class in a typemapper like descibed here
Edit:
Forget the typemapper, it does not work as the description suggests. One way i know for sure will work is to add the soap envelope using a little manual coding:
XElement snElement;
XmlSerializer mySerializer = new XmlSerializer(typeof(StartupNotification));
using (MemoryStream ms = new MemoryStream())
{
mySerializer.Serialize(ms, sn);
ms.Position = 0;
snElement = XElement.Load(ms);
}
XNamespace soapenv = "http://schemas.xmlsoap.org/soap/envelope/";
XElement soap = new XElement(soapenv + "Envelope",
new XElement(soapenv + "Body", snElement));
// use soap.ToString() to inspect the result

Related

C# Deserializes everything into anyField

I am working on a C# project where I need to read and write xml files. Therefor I use a 3rd party open source xml schema. As some of the xsd elements in this schema are open for non-schema specific extensions they provide so called "anyField" child elements like this:
<xs:complexType name="eTrackElements">
<xs:sequence>
...
<xs:any namespace="##other" processContents="strict" minOccurs="0" maxOccurs="unbounded">
</xs:any>
...
</xs:sequence>
</xs:complexType>
I have generated my C# code successfully using xsd.exe this way:
xsd.exe %XSD_IN% /c /language:CS /order /n:RailMLlib22 /o:%CS_OUT%
With that code I create an object structure and I am able to write/serialize it to a valid XML file. But when I try to read/deserialize this or any other (valid) xml file into an object tree, all elements inside "eTrackElements" don't land where they are suppossed to be, they all are deserialzed into the "anyField" although they are not special/extended and they are in the same namespace and they are standard elements of that xml schema.
The only way I can prevent this behaviour is commenting out the anyFields in my generated C# code:
// private System.Xml.XmlElement[] anyField;
Then everything works fine! But this is a bad workaround how can I solve this problem better?
As the code is 25k lines of code this may be relevant part:
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.33440")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace = "http://www.railml.org/schemas/2013")]
public partial class eTrackElements {
...
private tSpeedChange[] speedChangesField;
...
private System.Xml.XmlElement[] anyField;
...
[System.Xml.Serialization.XmlArrayAttribute(Order = 0)]
[System.Xml.Serialization.XmlArrayItemAttribute("speedChange", IsNullable = false)]
public tSpeedChange[] speedChanges
{
get
{
return this.speedChangesField;
}
set
{
this.speedChangesField = value;
}
}
...
[System.Xml.Serialization.XmlAnyElementAttribute(Order = 17)]
public System.Xml.XmlElement[] Any
{
get
{
return this.anyField;
}
set
{
this.anyField = value;
}
}
Example xml:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<railml xmlns:purl="http://purl.org/dc/elements/1.1/" xmlns="http://www.railml.org/schemas/2013">
<infrastructure>
<tracks>
<track type="mainTrack1" id="t1">
<trackTopology>
<trackBegin id="t1start" pos="0">
<connection id="c1" ref="c2"/>
</trackBegin>
<trackEnd id="t1end" pos="1306">
<connection id="c3" ref="c4"/>
</trackEnd>
<connections>
<switch pos="1306" id="sw196">
<connection course="right" orientation="outgoing" id="5" ref="6"/>
</switch>
</connections>
<crossSections>
<crossSection ocpRef="OCP1" pos="332" id="cs1">
<geoCoord coord="..."/>
</crossSection>
<crossSection ocpRef="OCP2" pos="810" id="cs2">
<geoCoord coord="..."/>
</crossSection>
<crossSection ocpRef="OCP3" pos="1279" id="cs3">
<geoCoord coord="..."/>
</crossSection>
</crossSections>
</trackTopology>
<trackElements>
<gradientChanges>
<gradientChange slope="0" pos="0" id="gr1"/>
<gradientChange slope="2" pos="329" id="gr2"/>
<gradientChange slope="3" pos="808" id="gr3"/>
<gradientChange slope="4" pos="1299" id="gr4"/>
<gradientChange slope="4" pos="1306" id="gr5"/>
</gradientChanges>
</trackElements>
</track>
</tracks>
</infrastructure>
</railml>
I import the xml file this way:
RailMLlib22.railml railML22 = null;
FileStream file = null;
try
{
file = new FileStream(filename, FileMode.Open);
railML22 = (RailMLlib22.railml)new XmlSerializer(typeof(RailMLlib22.railml)).Deserialize(file);
}
catch (Exception exc)
{
...
}
finally
{
file.Close();
}

Xml Output from Webservice Add a Xml Element C#

I have a webservice that is working without a problem (asmx running in iis). The only problem is that the client is really strict with the XML output. The current format is the following:
<ArrayOfVenda xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://213.63.189.121/webservicenos">
<venda>
<id>x</id>
<contact_moment>x</contact_moment>
</venda>
<venda>
<id>y</id>
<contact_moment>y</contact_moment>
</venda>
</ArrayOfVenda>
And it should be:
<ArrayOfVenda xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://213.63.189.121/webservicenos">
<root>
<venda>
<id>x</id>
<contact_moment>x</contact_moment>
</venda>
<venda>
<id>y</id>
<contact_moment>y</contact_moment>
</venda>
</root>
</ArrayOfVenda>
So the only thing is adding a XMLElement with the name root that contains the list venda. I'm having trouble in adding this element tho i really don't know how to go about it in my code. Here it is:
[WebMethod]
[return: System.Xml.Serialization.XmlElementAttribute("venda")]
public List<venda> getListaVendas(string dt_min, string dt_max)
{
List<venda> objVendaList = new List<venda>();
using (SqlConnection con = new SqlConnection(#"Data Source=server;Initial Catalog=db;User ID=user;password=password"))
{
using (SqlCommand cmd = new SqlCommand("SELECT * FROM dbo.vcnosadesoes_getlistavendas where contact_moment >='" + dt_min + "' AND contact_moment <DATEADD(dd, 1, '" + dt_max + "')", con))
{
con.Open();
SqlDataReader dr = cmd.ExecuteReader();
while (dr.Read())
{
var objVenda = new venda();
objVenda.id = dr["id"].ToString();
objVenda.contact_moment = dr["contact_moment"].ToString();
objVenda.nome = dr["nome"].ToString();
objVenda.pacote = dr["pacote"].ToString();
objVenda.telefone = dr["telefone"].ToString();
objVenda.codigo_wc = dr["codigo_wc"].ToString();
objVendaList.Add(objVenda);
}
dr.Close();
}
}
return objVendaList;
}
Any ideas what is the best method to add this element?
PS: I KNOW. I have to change the SQL Query because of SQL Injections i will get to that before putting it live don't worry. Also this Line:
[return: System.Xml.Serialization.XmlElementAttribute("venda")]
may be doing nothing i just put it up for some tests and never comment it out.
UPDATE : So the script from the client is still returning the error. After hours looking at the debugger i discovered what it need is this output:
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://213.63.189.121/webservicenos">
<venda>
<id>x</id>
<contact_moment>x</contact_moment>
</venda>
<venda>
<id>y</id>
<contact_moment>y</contact_moment>
</venda>
</root>
Since the XML has an extra level of testing (the <root> element between <ArrayOfVenda> and <venda>) the c# types you return have to have a similar structure to be successfully serialized in that form. Thus, introduce a new root type ArrayOfVenda:
[XmlRoot(ElementName = "ArrayOfVenda", Namespace = "http://213.63.189.121/webservicenos")]
public class ArrayOfVenda
{
[XmlArray("root")]
[XmlArrayItem("venda")]
public List<venda> VendaList { get; set; }
}
And return that from your getListaVendas method:
public ArrayOfVenda getListaVendas(string dt_min, string dt_max)
{
List<venda> objVendaList = new List<venda>();
// Fill in objVendaList as you do currently
return new ArrayOfVenda { VendaList = objVendaList };
}
Update
Given your new requirements for your XML output, you can define your ArrayOfVenda class as follows:
[XmlRoot(ElementName = "root", Namespace = "http://213.63.189.121/webservicenos")]
public class ArrayOfVenda
{
[XmlElement("venda")]
public List<venda> VendaList { get; set; }
}
[XmlRoot(...)] defines the name and namespace of ArrayOfVenda when it is the root XML element. [XmlElement("venda")] indicates that the VendaList will be formatted without an outer container element, and that each item is to be named <venda>.

Parsing/Deserialize XML data from API into object - There is an error in XML document (1871, 60)

I am working with an XML api, which returns the following:
<inventory>
<product inventoryId="1722474" externalReference="SM" site="global" total="0" allocated="0" available="0" frozen="0" onOrder="0" lastStockChangeId="505401" lastLineRequirementChangeId="0"/>
<product inventoryId="1722476" externalReference="PM" site="global" total="0" allocated="0" available="0" frozen="0" onOrder="0" lastStockChangeId="243256" lastLineRequirementChangeId="0"/>
.... 1000s of nodes ....
</inventory>
So, from this returned xml nodes, I am only interested in the following fields/attributes externalReference and available.
Therefore; I created the following class to describe the xml content I am going to deserialize/parse:
[XmlRoot(ElementName = "product")]
public class StockLevelProduct
{
[XmlAttribute(AttributeName = "externalReference")]
public string ExternalReference { get; set; }
[XmlAttribute(AttributeName = "available")]
public string Available { get; set; }
}
[XmlRoot(ElementName = "inventory")]
public class StockLevelResult
{
[XmlElement(ElementName = "product")]
public List<StockLevelProduct> Product { get; set; }
}
Then I put it all together like this:
// Init
StockLevelResult stockLevelResult;
// Anticipate errors
try
{
// Generate request url
string requestUrl = string.Format("{0}/remotewarehouse/inventory.xml?channel={1}",
apiUrl,
apiChannel);
// Call api
string apiResultXmlString = ApiGet(requestUrl);
// Fix api result xml string
if (!apiResultXmlString.Contains("<?xml"))
apiResultXmlString = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" + apiResultXmlString;
// Deserialize xml string to object
stockLevelResult = XmlParser.Parse<StockLevelResult>(apiResultXmlString);
}
catch (Exception ex)
{
Console.WriteLine("Failed to download stock levels - " + ex.Message);
}
Note* the returned xml string from the API server does not contain <?xml version="1.0" encoding="UTF-8"?> so I manually add it. Note sure if XmlParser.Parse requires it.
When this code executes; I get the following exception being thrown:
There is an error in XML document (1871, 60)
Any ideas why this isn't working? Is it an issue with the returned XML string? or the way I am trying to parse/deserialize it?
Try this
XmlSerializer xs = new XmlSerializer(typeof(StockLevelResult));
StringReader sReader = new StringReader(apiResultXmlString);
XmlTextReader reader = new XmlTextReader(sReader);
stockLevelResult = (StockLevelResult)xs.Deserialize(reader);
The exception was caused by bad data in the api XML response:
<product inventoryId="1726460" externalReference="V02002B&R"
site="global" total="0" allocated="0" available="0" frozen="0"
onOrder="0" lastStockChangeId="76231"
lastLineRequirementChangeId="0"/>
i.e. & in this attribute externalReference="V02002B&R"
I fixed it thanks to this answer like this:
// Call api
string apiResultXmlString = ApiGet(requestUrl);
// Fix api result xml string
if (!apiResultXmlString.Contains("<?xml"))
apiResultXmlString = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" + apiResultXmlString;
// Fix bad response data
string pattern = "(?<start>>)(?<content>.+?(?<!>))(?<end><)|(?<start>\")(?<content>.+?)(?<end>\")";
apiResultXmlString = System.Text.RegularExpressions.Regex.Replace(apiResultXmlString, pattern, m =>
m.Groups["start"].Value +
System.Web.HttpUtility.HtmlEncode(System.Web.HttpUtility.HtmlDecode(m.Groups["content"].Value)) +
m.Groups["end"].Value);

Extracting values from XML returned by azure service management API

i have tried several methods of trying to extract values from an XML file but none of them seem to work. I am using C#. The XML Is as follows
<?xml version="1.0" encoding="utf-8"?>
<HostedService xmlns="http://schemas.microsoft.com/windowsazure">
<Url>hosted-service-url</Url>
<ServiceName>hosted-service-name</ServiceName>
<HostedServiceProperties>
<Description>description</Description>
<Location>location</Location>
<AffinityGroup>affinity-group</AffinityGroup>
<Label>label</Label>
</HostedServiceProperties>
</HostedService>
I would like to retrieve
hosted-service-url,
hosted-service-name,
description,
location,
affinity-group and
label
What would be the best of way of retrieving these values?
Edit :
Thanks L.B that method works perfectly. However i have just been told i will have to use the larger XML that is below.
<?xml version="1.0" encoding="utf-8"?>
<HostedService xmlns="http://schemas.microsoft.com/windowsazure">
<Url>hosted-service-url</Url>
<ServiceName>hosted-service-name</ServiceName>
<HostedServiceProperties>
<Description>description</Description>
<Location>location</Location>
<AffinityGroup>affinity-group</AffinityGroup>
<Label>base-64-encoded-name-of-the-service</Label>
</HostedServiceProperties>
<Deployments>
<Deployment>
<Name>deployment-name</Name>
<DeploymentSlot>deployment-slot</DeploymentSlot>
<PrivateID>deployment-id</PrivateID>
<Status>deployment-status</Status>
<Label>base64-encoded-deployment-label</Label>
<Url>deployment-url</Url>
<Configuration>base-64-encoded-configuration-file</Configuration>
<RoleInstanceList>
<RoleInstance>
<RoleName>role-name</RoleName>
<InstanceName>role-instance-name</InstanceName>
<InstanceStatus>instance-status</InstanceStatus>
</RoleInstance>
</RoleInstanceList>
<UpgradeDomainCount>upgrade-domain-count</UpgradeDomainCount>
<RoleList>
<Role>
<RoleName>role-name</RoleName>
<OsVersion>operating-system-version</OsVersion>
</Role>
</RoleList>
<SdkVersion>sdk-version-used-to-create-package</SdkVersion>
<InputEndpointList>
<InputEndpoint>
<RoleName>role-name</RoleName>
<Vip>virtual-ip-address</Vip>
<Port>port-number</Port>
</InputEndpoint>
…
</InputEndpointList>
<Locked>deployment-write-allowed-status</Locked>
<RollbackAllowed>rollback-operation-allowed</RollbackAllowed>
</Deployment>
</Deployments>
</HostedService>
My final question is, there is several repeated tags, such as ,
how can i differentiate between them?
you can use Xml to Linq to parse your xml string. For ex,
var xElem = XElement.Load(new StringReader(xml));
var ns = XNamespace.Get("http://schemas.microsoft.com/windowsazure");
var obj = new
{
ServiceName = xElem.Descendants(ns + "ServiceName").First().Value,
Description = xElem.Descendants(ns + "Description").First().Value,
};
or you can use XmlSerializer
XmlSerializer xs = new XmlSerializer(typeof(HostedService), "http://schemas.microsoft.com/windowsazure");
var obj2 = (HostedService)xs.Deserialize(new StringReader(xml));
public class HostedService
{
public string Url;
public string ServiceName;
public HostedServiceProperties HostedServiceProperties;
}
public class HostedServiceProperties
{
public string Description;
public string Location;
public string AffinityGroup;
public string Label;
}
Maybe you can try samples from XmlDocument ( http://msdn.microsoft.com/en-us/library/d271ytdx.aspx) and and LINQ to XML -( http://msdn.microsoft.com/en-us/library/bb669152.aspx) first and than apply it to your case.

How to control namespace in .NET SoapFormatter?

I am writing some code that needs to be backwards compatible with EXISTING remoting code that is using SOAP to serialize some objects.
My difficulty is that I have had to move some objects to new assemblies, so the remoting is broken.
For example, I serialize an object using the .NET SoapFormatter like this:
Person p=new Person();
string path=#"c:\myfile.soap";
using (System.IO.FileStream fs = new System.IO.FileStream(path, System.IO.FileMode.OpenOrCreate, System.IO.FileAccess.Write))
{
System.Runtime.Serialization.Formatters.Soap.SoapFormatter
f = new System.Runtime.Serialization.Formatters.Soap.SoapFormatter();
f.Serialize(fs, p);
fs.Close();
}
The resulting xml looks like this:
<SOAP-ENV:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:clr="http://schemas.microsoft.com/soap/encoding/clr/1.0" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<a1:Person id="ref-1" xmlns:a1="http://schemas.microsoft.com/clr/nsassem/Serialization/dotneat_net.Serialization%2C%20Version%3D0.0.0.0%2C%20Culture%3Dneutral%2C%20PublicKeyToken%3Dnull">
<FirstName id="ref-3">Joe</FirstName>
<LastName id="ref-4">Doe</LastName>
<_Address id="ref-5">dotneat.net Street, Zaragoza, Spain</_Address>
<_ZIPCode id="ref-6">50007</_ZIPCode>
</a1:Person>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
In that XML, I'd like to have some control over the xmlns on the a1:Person object:
<a1:Person id="ref-1" xmlns:a1="http://schemas.microsoft.com/clr/nsassem/Serialization/dotneat_net.Serialization%2C%20Version%3D0.0.0.0%2C%20Culture%3Dneutral%2C%20PublicKeyToken%3Dnull">
The reason is that my new Person object is not in the same assembly as the original object. So, later when deserialization occurs (in the older projects), it fails because of wrong assembly.
How can I control the text in the xmlns? I have tried a few things such as using the [SoapType Namespace="xxx"] attribute on the class that is being serialized. No luck.
I'd prefer to avoid modifying the XML manually.
I was able to set the namespace using the SoapType attribute.
[Serializable]
[System.Runtime.Remoting.Metadata.SoapType(XmlNamespace = "MY_NAMESPACE")]
public class Person
{
public string FirstName;
public string LastName;
public string _Address;
public string _ZIPCode;
}
This generated the following serialized XML:
<SOAP-ENV:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:clr="http://schemas.microsoft.com/soap/encoding/clr/1.0" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<i2:Person id="ref-1" xmlns:i2="MY_NAMESPACE">
<FirstName id="ref-3">John</FirstName>
<LastName id="ref-4">Doe</LastName>
<_Address id="ref-5">otneat.net Street, Zaragoza, Spain</_Address>
<_ZIPCode id="ref-6">50007</_ZIPCode>
</i2:Person>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
However, I wasn't able to verify the deserialization on the other side of the remoting channel. Hopefully, that helps you.
Have you tried SoapAttribute? It has an XmlNamespace property. May be that will work. You can also use Reflector to see what SoapFormatter.Serialize is doing. You may get some ideas that you can try.
While the solution from Randy does work as expected. I did want to preserve the original a1 notation:
<a1:Person id="ref-1"
So I ended up implementing:
[Serializable]
public class Person : System.Runtime.Serialization.ISerializable
{
public string FirstName;
public string LastName;
public string _Address;
public string _ZIPCode;
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AssemblyName = "ConsoleApp";
System.Reflection.MemberInfo[] members = this.GetType().GetMembers();
foreach (var member in members)
{
if (member.MemberType == System.Reflection.MemberTypes.Field)
{
System.Reflection.FieldInfo field = ((System.Reflection.FieldInfo)member);
string name = member.Name;
object value = field.GetValue(this);
info.AddValue(name, value);
}
}
}
}
Turns out there is an even easier solution:
SoapFormatter soapFormatter = new SoapFormatter();
soapFormatter.AssemblyFormat = FormatterAssemblyStyle.Simple;
soapFormatter.Serialize(fs, p);

Categories