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();
}
Related
I need to validate some generic sensor input. The requirement is, that the validation cannot happen in my code but with a external validator like xsd from outside the codebase to give users the ability to swap the validation logic without needing to code or recompile the application.
I know that the sensor input is only valid for one specific case and therefore would like to generate the xsd from an Instance of a class, that exists at runtime, that was user validated, to get the valid restrictions.
I tried the Idea from this question, however this only works on types and not on instances of classes.
Therefore my question: Is there a way to take a runtime instance of a C# class and convert it to an xsd that has the values of the properties as the only valid restrictions?
Update:
to clarify: What I have is a class like this:
public sealed class Sensor
{
public int Data { get; set; }
public int otherData { get; set; }
public int MoreData { get; set; }
}
the class gets instanciated somewhere (e.g. like this):
var se = new Sensor()
{
Data = 5,
otherData = 10,
MoreData = 15
};
When I now try to create an xsd using something like the following function:
var schemas = new XmlSchemas();
var exporter = new XmlSchemaExporter(schemas);
var mapping = new XmlReflectionImporter().ImportTypeMapping(typeof(Person));
exporter.ExportTypeMapping(mapping);
var schemaWriter = new StringWriter();
foreach (XmlSchema schema in schemas)
{
schema.Write(schemaWriter);
}
return schemaWriter.ToString();
I receive some xsd like this:
<?xml version="1.0" encoding="utf-8"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="sensor">
<xs:complexType>
<xs:sequence>
<xs:element name="Data" type="xs:integer" />
<xs:element name="otherData" type="xs:integer" />
<xs:element name="moreData" type="xs:integer" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
However this is far from what I want to archieve. I would like to have the proper restrictions built into it (it should look something like this):
<?xml version="1.0" encoding="utf-8"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="sensor">
<xs:complexType>
<xs:sequence>
<xs:element name="Data">
<xs:simpleType>
<xs:restriction base="xs:integer">
<xs:enumeration value="5"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="otherData">
<xs:simpleType>
<xs:restriction base="xs:integer">
<xs:enumeration value="10"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="moreData">
<xs:simpleType>
<xs:restriction base="xs:integer">
<xs:enumeration value="15"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
I could obviously go ahead load the generated file into memory, strip some attributes and change how the xsd should look like, but this feels wrong because of the following things:
By me defining Rules to how the xsd should look like I take away
flexibility that I would like to have.
This approach seems quite errorprone to me because it seems like basically a little better than direct string manipulation.
This extra code would make my already large code way complexer and harder to understand.
To sum up: I need either a library or a really clever function that can create a xsd like the one above based on the runitme info I have on the class without writing a lot of things to manipulate the xml directly to avoid errorprone or wrong assumptions about the future usage of the validation.
I took your generate schema and added details using Xml Linq. See code below
using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Serialization;
using System.Xml.Schema;
using System.IO;
namespace ConsoleApplication131
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
Sensor se = new Sensor()
{
Data = 5,
otherData = 10,
MoreData = 15
};
XmlSchemas schemas = new XmlSchemas();
XmlSchemaExporter exporter = new XmlSchemaExporter(schemas);
XmlTypeMapping mapping = new XmlReflectionImporter().ImportTypeMapping(typeof(Sensor));
exporter.ExportTypeMapping(mapping);
StringWriter schemaWriter = new StringWriter();
foreach (XmlSchema schema in schemas)
{
schema.Write(schemaWriter);
}
XDocument doc = XDocument.Parse(schemaWriter.ToString());
XElement root = doc.Root;
XNamespace xs = root.GetNamespaceOfPrefix("xs");
foreach (XElement _class in doc.Descendants(xs + "complexType"))
{
List<XElement> elements = _class.Descendants(xs + "element").ToList();
if (elements.Count > 0)
{
XElement complexType = new XElement(xs + "complexType");
_class.Add(complexType);
XElement sequence = new XElement(xs + "sequence");
complexType.Add(sequence);
foreach (var prop in se.GetType().GetProperties())
{
string name = prop.Name;
string value = prop.GetValue(se, null).ToString();
XElement element = elements.Where(x => (string)x.Attribute("name") == name).FirstOrDefault();
string strType = (string)element.Attribute("type");
XElement newElement = new XElement(xs + "simpleType", new object[] {
new XElement(xs + "restriction", new object[] {
new XAttribute("base", strType),
new XElement(xs + "enumeration", new XAttribute("value", value))
})
});
sequence.Add(newElement);
}
}
}
doc.Save(FILENAME);
}
}
public sealed class Sensor
{
public int Data { get; set; }
public int otherData { get; set; }
public int MoreData { get; set; }
}
}
I have an xsd file that describes a small schema. I created a C# file with the help of xsd.exe (I ran this command in the developer command prompt: xsd.exe smallSchema.xsd /classes /language:CS) to be able to easily (de-)serialize it. I took an xml file that complies to that schema and tried to deserialize it by using the generated code. But i noticed that data loss occurs!
Can anyone point me to the cause of that data loss?
This is the xml file that I want to deserialize:
<?xml version="1.0" encoding="UTF-8"?>
<top-element>
<complex-elem type="plain-number">1</complex-elem>
<top-elem-name>myTopElement</top-elem-name>
</top-element>
The following code will deserialize it, then serialize it again and overwrite the file:
XmlSerializer serializer = new XmlSerializer(typeof(topelement));
string path = ... // path of the file on my disk
topelement rawData = null;
using (FileStream reader = new FileStream(path, FileMode.Open))
{
rawData = (topelement)serializer.Deserialize(reader);
}
XmlSerializerNamespaces noNamespace
= new XmlSerializerNamespaces(new XmlQualifiedName[] { new XmlQualifiedName("", "") });
// I use it to prevent adding a namespace during serializing
using (XmlWriter wr = XmlWriter.Create(path))
{
serializer.Serialize(wr, rawData, noNamespace);
}
But the overwritten file looks like this:
<?xml version="1.0" encoding="utf-8"?>
<top-element>
<complex-elem />
<top-elem-name>myTopElement</top-elem-name>
</top-element>
I.e. the data inside complex-elem got lost! Debugging shows that the deserialized content already contains null where it shouldn't (see picture).
This is the xsd schema:
<?xml version="1.0" encoding="utf-8"?>
<xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="top-element">
<xs:complexType>
<xs:sequence>
<xs:element ref="complex-elem" />
<xs:element ref="top-elem-name" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="complex-elem">
<xs:complexType>
<xs:choice>
<xs:element ref="plain-number" />
</xs:choice>
</xs:complexType>
</xs:element>
<xs:element name="plain-number" type="xs:string" />
<xs:element name="top-elem-name" type="xs:string" />
</xs:schema>
And here is the result of calling xsd.exe (the comments are German):
//------------------------------------------------------------------------------
// <auto-generated>
// Dieser Code wurde von einem Tool generiert.
// Laufzeitversion:4.0.30319.42000
//
// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
// der Code erneut generiert wird.
// </auto-generated>
//------------------------------------------------------------------------------
using System.Xml.Serialization;
//
// Dieser Quellcode wurde automatisch generiert von xsd, Version=4.6.1055.0.
//
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.6.1055.0")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true)]
[System.Xml.Serialization.XmlRootAttribute("top-element", Namespace="", IsNullable=false)]
public partial class topelement {
private complexelem complexelemField;
private string topelemnameField;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute("complex-elem")]
public complexelem complexelem {
get {
return this.complexelemField;
}
set {
this.complexelemField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute("top-elem-name")]
public string topelemname {
get {
return this.topelemnameField;
}
set {
this.topelemnameField = value;
}
}
}
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.6.1055.0")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true)]
[System.Xml.Serialization.XmlRootAttribute("complex-elem", Namespace="", IsNullable=false)]
public partial class complexelem {
private string itemField;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute("plain-number")]
public string Item {
get {
return this.itemField;
}
set {
this.itemField = value;
}
}
}
The XML sample you try to deserialize doesn't validate against the XSD schema you provided.
If you try to validate the XML, you'll get three validation errors:
Cvc-complex-type.3.2.2: Attribute 'type' Is Not Allowed To Appear In Element 'complex-elem'., Line '2', Column '39'.
Cvc-complex-type.2.3: Element 'complex-elem' Cannot Have Character [children], Because The Type's Content Type Is Element-only., Line
'2', Column '55'.
Cvc-complex-type.2.4.b: The Content Of Element 'complex-elem' Is Not Complete. One Of '{plain-number}' Is Expected., Line '2', Column
'55'.
So either change your XML to the following one:
<?xml version="1.0" encoding="UTF-8"?>
<top-element>
<complex-elem>
<plain-number>1</plain-number>
</complex-elem>
<top-elem-name>myTopElement</top-elem-name>
</top-element>
... or change your complex-elem schema definition to this one:
<xs:element name="complex-elem">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="type" type="xs:string" />
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
I have static method, which i use to validate a XML File against a XSD File. This works fine, until there is an XSD File which includes another XSD File.
Example, where i got troubles:
TYPES.XSD:
<xs:simpleType name="MY_AMOUNT">
<xs:restriction base="xs:decimal">
<xs:maxInclusive value="999999999999.99"/>
<xs:minInclusive value="-999999999999.99"/>
<xs:totalDigits value="14"/>
<xs:fractionDigits value="2"/>
</xs:restriction>
</xs:simpleType>
MAIN.XSD:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
<xs:include schemaLocation="TYPES.xsd"/>
<xs:element name="ROOT">
<xs:complexType>
<xs:sequence>
<xs:element ref="SOMEREF1"/>
<xs:element ref="SOMEREF2"/>
<xs:element name="AMOUNT" type="MY_AMOUNT" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
VALIDATION CODE:
public static class XmlUtils
{
private static string Errors = string.Empty;
public static bool ValidateAgainstXSD(string xmlFilePath, string xsdFilePath, ref string message)
{
try
{
var settings = new XmlReaderSettings();
settings.ValidationType = ValidationType.Schema;
settings.ValidationFlags = XmlSchemaValidationFlags.ProcessInlineSchema
| XmlSchemaValidationFlags.ProcessInlineSchema
| XmlSchemaValidationFlags.ReportValidationWarnings;
settings.Schemas.Add(null, xsdFilePath);
settings.Schemas.Compile();
settings.ValidationEventHandler += (sender, args) =>
{
if (args.Severity == XmlSeverityType.Error)
{
Errors += args.Message + "\n";
}
};
using (var reader = XmlReader.Create(xmlFilePath, settings))
{
while (reader.Read()) { }
}
message = Errors ?? string.Empty;
return string.IsNullOrEmpty(Errors);
}
catch (Exception e)
{
message = "# error validating xml file: " + e.Message;
return false;
}
}
}
Somehow it seems i have to specify the path of the included XSD File but i have no idea where.
The error occurs at settings.Schemas.Compile(); , where it says that the type "MY_AMOUNT" is not declared. I read about custom XmlResolvers but to be honest i didn't get that working.
If this is important for an answer: The xsd files are always located in the same directory!
The method is called likes this:
string msg = string.Empty;
string basedir = #"C:\Temp";
string xml = Path.Combine(basedir, "XML_FILE.xml");
string xsd = Path.Combine(basedir, "MAIN.xsd");
if (XmlUtils.ValidateAgainstXSD(xml, xsd, ref msg))
{
// do some work
}
else
{
Console.WriteLine(msg);
}
Console.ReadLine();
Any help is highly appreciated - Thank you!
UPDATE 2016-12-05:
I wrote my own XmlUrlResolver, to see what happens behind the scenes:
internal class XUrlResolver : XmlUrlResolver
{
public override object GetEntity(Uri absoluteUri, string role, Type ofObjectToReturn)
{
return base.GetEntity(absoluteUri, role, ofObjectToReturn);
}
public override Uri ResolveUri(Uri baseUri, string relativeUri)
{
return base.ResolveUri(baseUri, relativeUri);
}
}
And the i just try to do:
XmlSchemaSet xset = new XmlSchemaSet();
xset.XmlResolver = new XUrlResolver();
xset.Add("", xsdFilePath);
xset.Compile();
What happens now (on line xset.Add):
XmlUrlResolver.ResolveUri(null,"C:\\Temp\\MAIN.XSD") --> {file:///C:/Temp/MAIN.xsd}
XmlUrlResolver.ResolveUri(null,"C:\\Temp\\MAIN.XSD") --> {file:///C:/Temp/MAIN.xsd}
XmlUrlResolver.GetEntity({file:///C:/Temp/MAIN.xsd}) --> Filestream to MAIN.xsd
XmlUrlResolver.ResolveUri({file:///C:/Temp/MAIN.xsd},"TYPES.XSD") --> {file:///C:/Temp/TYPES.xsd}
XmlUrlResolver.GetEntity({file:///C:/Temp/TYPES.xsd}) --> Filestream to TYPES.xsd
Looks good to me (except the first 2 Calls are equal!?!) - the path to TYPES.XSD is resolved as it should.
Nevertheless, xset.Compile() throws an Exception: "Type MY_AMOUNT is not declared"
And i have no idea why :/
First you need to make your xsd files valid.
Types.xsd (added schema root element and xs namespace)
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:simpleType name="MY_AMOUNT">
<xs:restriction base="xs:decimal">
<xs:maxInclusive value="999999999999.99"/>
<xs:minInclusive value="-999999999999.99"/>
<xs:totalDigits value="14"/>
<xs:fractionDigits value="2"/>
</xs:restriction>
</xs:simpleType>
</xs:schema>
Main.xsd (removed invalid refs).
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
<xs:include schemaLocation="TYPES.xsd"/>
<xs:element name="ROOT">
<xs:complexType>
<xs:sequence>
<xs:element name="AMOUNT" type="MY_AMOUNT" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
After that, given that both xsd files are in the same directory, your schemas will compile fine.
I ran into this very same issue.
I'm not suggesting this is the correct answer, but I got around it by setting the Environment.CurrentDirectory property to be the path where the included XSDs were located. Then it all processed just fine.
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 created an .xsd from an xml file using XSD.Exe from the Visual Studio Tools (now Win 7 SDK Tools).
My .xsd file:
<?xml version="1.0" encoding="utf-8"?>
<xs:schema id="SceneFile" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xs:element name="Scene">
<xs:complexType>
<xs:sequence>
<xs:element name="VNESceneName" type="xs:string" minOccurs="0" maxOccurs="1"/>
<xs:element name="VNEPlayerName" type="xs:string" minOccurs="0" maxOccurs="1"/>
<xs:element name="VNEButtons" minOccurs="0" maxOccurs="1">
(cutted sicne it's too long)
I then used XSD2Code (an add-in for VS) to create an .Designer.cs file:
namespace VNEngine
{
using System;
using System.Diagnostics;
using System.Xml.Serialization;
using System.Collections;
using System.Xml.Schema;
using System.ComponentModel;
using System.IO;
using System.Text;
using System.Collections.Generic;
public partial class Scene
{
private string vNESceneNameField;
private string vNEPlayerNameField;
private List<SceneVNEButton> vNEButtonsField;
private SceneVNETextBox vNETextBoxField;
private SceneVNEBackground vNEBackgroundField;
private List<SceneVNESprite> vNESpritesField;
private SceneVNEMusic vNEMusicField;
private static System.Xml.Serialization.XmlSerializer serializer;
public Scene()
{
this.vNEMusicField = new SceneVNEMusic();
this.vNESpritesField = new List<SceneVNESprite>();
this.vNEBackgroundField = new SceneVNEBackground();
this.vNETextBoxField = new SceneVNETextBox();
this.vNEButtonsField = new List<SceneVNEButton>();
}
public string VNESceneName
{
get
{
return this.vNESceneNameField;
}
set
{
this.vNESceneNameField = value;
}
}
(cutted sicne it's too long)
Now I want to create a "Scene"(the root xml tag) in my C# code:
Scene testscene = new Scene();
testscene.VNEPlayerName = "hallo";
All up to this point is Working, but when i want to save or Serialize (or Deserialize) like this:
testscene.SaveToFile(#"Content/Scenes/testscene.xml");
And now when executing I get an Error at the Line (in SceneFiles.designer.cs)
private static System.Xml.Serialization.XmlSerializer Serializer
{
get
{
if((serializer == null))
{
---> serializer = new System.Xml.Serialization.XmlSerializer(typeof(Scene));
}
return serializer;
}
}
And my Error goes like this:
//
Eine nicht behandelte Ausnahme des Typs
"System.InvalidOperationException" ist in System.Xml.dll aufgetreten.
Zusätzliche Informationen: Fehler beim Reflektieren des Typs
'VNEngine.Scene'.
//
My rough english translation:
A not handled Exception: System.InvalidOperationException appeared in
System.Xml.dll Additional Information: Error at Reflecting the type
'VNEngine.Scene'
//
Does anyone know what I made wrong?
The same thing worked with an smaller not as complex and not with xsd.exe generatet .xsd stylesheet.
Ok, i found it, XSD2Code does have an option GenerateXMLAttributes which needs to be set true.