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);
Related
I know that most of the time, the Deserialize method of XmlSerializer will complain if there's something wrong (for example, if there is a typo). However, I've found an example where it doesn't complain, when I would have expected it to; and I'd like to know if there's a way of being told about the problem.
The example code below contains 3 things: an good example which works as expected, and example which would complain (commented out) and an example which does not complain, which is the one I want to know how to tell that there is something wrong.
Note: I appreciate that one possible route would be XSD validation; but that really feels like a sledgehammer to crack what seems like a simpler problem. For example, if I was writing a deserializer which had unexpected data that it didn't know what to do with, I'd make my code complain about it.
I've used NUnit (NuGet package) for assertions; but you don't really need it, just comment out the Assert lines - you can see what I'm expecting.
using System.IO;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using NUnit.Framework;
public static class Program
{
public static void Main()
{
string goodExampleXml = #"<?xml version=""1.0"" encoding=""utf-8""?><Example><Weathers><Weather>Sunny</Weather></Weathers></Example>";
var goodExample = Load(goodExampleXml);
Assert.That(goodExample, Is.Not.Null);
Assert.That(goodExample.Weathers, Is.Not.Null);
Assert.That(goodExample.Weathers, Has.Length.EqualTo(1));
Assert.That(goodExample.Weathers.First(), Is.EqualTo(Weather.Sunny));
string badExampleXmlWhichWillComplainXml = #"<?xml version=""1.0"" encoding=""utf-8""?><Example><Weathers><Weather>Suny</Weather></Weathers></Example>";
// var badExampleWhichWillComplain = Load(badExampleXmlWhichWillComplainXml); // this would complain, quite rightly, so I've commented it out
string badExampleXmlWhichWillNotComplain = #"<?xml version=""1.0"" encoding=""utf-8""?><Example><Weathers><Weathe>Sunny</Weathe></Weathers></Example>";
var badExample = Load(badExampleXmlWhichWillNotComplain);
Assert.That(badExample, Is.Not.Null);
Assert.That(badExample.Weathers, Is.Not.Null);
// clearly, the following two assertions will fail because I mis-typed the tag name; but I want to know there has been a problem before this point.
Assert.That(badExample.Weathers, Has.Length.EqualTo(1));
Assert.That(badExample.Weathers.First(), Is.EqualTo(Weather.Sunny));
}
private static Example Load(string serialized)
{
byte[] byteArray = Encoding.UTF8.GetBytes(serialized);
var xmlSerializer = new XmlSerializer(typeof(Example));
using var stream = new MemoryStream(byteArray, false);
return (Example)xmlSerializer.Deserialize(stream);
}
}
public enum Weather
{
Sunny,
Cloudy,
Rainy,
Windy,
Stormy,
Snowy,
}
public class Example
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "Serialized XML")]
[XmlArray("Weathers")]
[XmlArrayItem("Weather")]
public Weather[] Weathers { get; set; }
}
Having looked at Microsoft's published source code for XmlSerializer, it became apparent that there are events that you can subscribe to (which is what I was hoping for); but they aren't exposed on the XmlSerializer itself... you have to inject a struct containing them into the constructor.
So I've been able to modify the code from the question to have an event handler which gets called when an unknown node is encountered (which is exactly what I was after). You need one extra using, over the ones given in the question...
using System.Xml;
and then here is the modified "Load" method...
private static Example Load(string serialized)
{
XmlDeserializationEvents events = new XmlDeserializationEvents();
events.OnUnknownNode = (sender, e) => System.Diagnostics.Debug.WriteLine("Unknown Node: " + e.Name);
var xmlSerializer = new XmlSerializer(typeof(Example));
using var reader = XmlReader.Create(new StringReader(serialized));
return (Example)xmlSerializer.Deserialize(reader, events);
}
So now I just need to do something more valuable than just write a line to the Debug output.
Note that more events are available, as described on the XmlDeserializationEvents page, and I'll probably pay attention to each of them.
I tested following and it works
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string xml =#"<?xml version=""1.0"" encoding=""utf-8"" ?>
<Example>
<Weathers>Sunny</Weathers>
<Weathers>Cloudy</Weathers>
<Weathers>Rainy</Weathers>
<Weathers>Windy</Weathers>
<Weathers>Stormy</Weathers>
<Weathers>Snowy</Weathers>
</Example>";
StringReader sReader = new StringReader(xml);
XmlReader reader = XmlReader.Create(sReader);
XmlSerializer serializer = new XmlSerializer(typeof(Example));
Example example = (Example)serializer.Deserialize(reader);
}
}
public enum Weather
{
Sunny,
Cloudy,
Rainy,
Windy,
Stormy,
Snowy,
}
public class Example
{
[XmlElement("Weathers")]
public Weather[] Weathers { get; set; }
}
}
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
I'm using visual studio for windows phone and my code for the XML reader does not work when there is attributes in the parent of the XML data.
My C# code
namespace youtube_xml
{
public partial class MainPage : PhoneApplicationPage
{
// Constructor
public MainPage()
{
InitializeComponent();
SupportedOrientations = SupportedPageOrientation.PortraitOrLandscape;
}
private void listBox1_Loaded(object sender, RoutedEventArgs e)
{
var element = XElement.Load("Authors.xml");
var authors =
from var in element.Descendants("feed")
select new Authors
{
AuthorName = var.Attribute("scheme").Value,
};
listBoxAuthors.DataContext = authors;
}
public ImageSource GetImage(string path)
{
return new BitmapImage(new Uri(path, UriKind.Relative));
}
}
}
The Working XML data
<?xml version='1.0' encoding='UTF-8'?>
<feed>
<category scheme='http://schemas.google.com/g/2005#kind'/>
</feed>
NOT working data (note: the attribute "xmlns" in the root element "feed")
<?xml version='1.0' encoding='UTF-8'?>
<feed xmlns='http://www.w3.org/2005/Atom' >
<category scheme='http://schemas.google.com/g/2005#kind'/>
</feed>
Welcome to the world of XML namespaces! The problem isn't the fact that "there's an attribute" - it's the fact that it's causing everything below it to be in a namespace. You can no longer say .Attribute("scheme") because that only looks for things in the empty namespace. Namespaces are used via a contraption based on operator overloading:
XNamespace atom = "http://www.w3.org/2005/Atom'";
// And now you can say:
.Descendants(atom + "feed")
.Attribute(atom + "scheme")
Et cetera. The ability to assign a string into an XNamespace variable is thanks to an implicit conversion operator. The + here actually constructs an XName (which, by the way, also has an implicit conversion from string - that's why you plain .Elements("feed") works even though the parameter type is not string)
Handy tip: You can cast an attribute into certain types instead of using .Value, for instance (string)foo.Attribute(atom + "scheme"). It also works with a bunch of other types, for instance int.
I made a request to a third party API and it gives me the following response in XML.
<?xml version="1.0" ?>
<abc>
<xyz>
<code>-112</code>
<message>No such device</message>
</xyz>
</abc>
I read this using this code.
XmlDocument doc = new XmlDocument();
doc.Load("*** url ***");
XmlNode node = doc.SelectSingleNode("/abc/xyz");
string code = node.SelectSingleNode("code").InnerText;
string msg = node.SelectSingleNode("message").InnerText;
Response.Write("Code: " + code);
Response.Write("Message: "+ msg);
But I get an error on this line:
string code = node.SelectSingleNode("code").InnerText;
Error is:
Object reference not set to an instance of an object.
I changed the first line of your XML file into:
<?xml version="1.0"?>
to make it valid XML. With this change, your code works for me. Without the change, the parser throws an exception.
You can use LINQ to XML (if confortable):
XDocument doc = XDocument.Load(url);
var selectors = (from elements in doc.Elements("abc").Elements("xyz")
select elements).FirstOrDefault();
string code = selectors.Element("code").Value;
string msg = selectors.Element("message").Value;
As you've given it, there doesn't seem to be anything wrong with your code Edit : Your declaration is wrong, as svinja pointed out, and your xml won't even load into the XmlDocument.
However, I'm guessing that your xml is more complicated, and there is at least one namespace involved, which would cause the select to fail.
It isn't pretty, but what you can do is use namespace agnostic xpath to locate your nodes to avoid using a XmlNamespaceManager:
XmlDocument doc = new XmlDocument();
doc.Load("*** url ***");
XmlNode node = doc.SelectSingleNode("/*[local-name()='abc']/*[local-name()='xyz']");
string code = node.SelectSingleNode("*[local-name()='code']").InnerText;
string msg = node.SelectSingleNode("*[local-name()='message']").InnerText;
Response.Write("Code: " + code);
Response.Write("Message: "+ msg);
Edit - Elaboration
Your code works fine if you correct the declaration to <?xml version="1.0"?>
However, if you introduce namespaces into the mix, your code will fail unless you use namespace managers appropriately.
My agnostic xpath above will also parse an xml document like so:
<?xml version="1.0"?>
<abc xmlns="foo">
<xyz xmlns="bar">
<code xmlns="bas">-112</code>
<message xmlns="xyz">No such device</message>
</xyz>
</abc>
<?xml version="1.0">
<abc>
<xyz>
<code>-112</code>
<message> No such device </message>
</xyz>
</abc>
try to set a list:
XmlNodeList nodeList = root.SelectNodes("/abc/xyz");
then read all the nodes and get their text:
foreach(XmlNode node in nodeList)
{
if(node.Name == "code")
{
string code = node.InnerText;
}
else
if(node.Name == "message")
{
string msg = node.InnerText;
}
}
[XmlRoot("abc")]
public class Entity
{
[XmlElement("xyz")]
public SubEntity SubEntity { get; set; }
}
public class SubEntity
{
[XmlElement("code")]
public string Code { get; set; }
[XmlElement("message")]
public string Message { get; set; }
}
And use standart xmlserializer
var xmlSerializer = new XmlSerializer(typeof(Entity));
var result = xmlSerializer.Deserialize(new XmlTextReader("*** url ***"));
Response.Write("Code: " + result.SubEntity.Code);
Response.Write("Message: "+ result.SubEntity.Message);
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.