I am trying to create a waypoint generator using windows forms, that allows a user to create waypoint & path data via a GUI & output the data to an XML file. I've chosen to take advantage of the built in c# XML serialization feature, but have been unable to format the XML output in the way required by the client.
A stripped version of the waypoint data object would look something like the following:
// Waypoint data class
[XmlRoot("RootNode")]
public class WaypointProjectData
{
[XmlElement("Map")] // Also tried to use XmlElementAttribute,
[XmlAttribute("file")] // XmlAttributeAttribute, and many variations
// of these qualifiers, with no success
public string m_szMapImageFileName;
[XmlAttribute("width")]
public int m_iWidth;
[XmlAttribute("height")]
public int m_iHeight;
[XmlArray("Nodes")]
public ArrayList m_aoNodes;
WaypointProjectData()
{
m_szMapImageFileName = "map.png";
m_aoNodes = new ArrayList();
}
}
The client requires that the XML output conforms with the following layout/format:
<RootNode>
<Map file="map.png" width="100" height="100" />
<OtherData var="variable" data="10" />
<Nodes>
<Node x="10" y="30" />
<Node x="30" y="20" /> // etc...
</Nodes>
</RootNode>
Some clear examples on how to do this using c# xml serialization would be very helpful. Thanks!
When I have to serialize something in a specific Xml format like this, I start with a schema (inferred if necessary, hand-crafted if known). Then I use the VS xsd tool to create the serialization classes (and yes, I know xsd can be used for the inferring schema part—I just never bother with it for that purpose).
So if you have an xsd file like this one created from your example (note that I specified data types as much as possible—this helps the xsd tool to use the appropriate types for properties):
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">
<xs:element name="RootNode">
<xs:complexType>
<xs:sequence>
<xs:element name="Map">
<xs:complexType>
<xs:attribute name="file" type="xs:string"/>
<xs:attribute name="width" type="xs:decimal"/>
<xs:attribute name="height" type="xs:decimal"/>
</xs:complexType>
</xs:element>
<xs:element name="OtherData">
<xs:complexType>
<xs:attribute name="var" type="xs:string"/>
<xs:attribute name="data" type="xs:decimal"/>
</xs:complexType>
</xs:element>
<xs:element name="Nodes" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element name="Node" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="x" type="xs:decimal"/>
<xs:attribute name="y" type="xs:decimal"/>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
If you name this Root.xsd, then you can go to the VS command line and run
xsd Root.xsd /c /n:myProject.Xml
That'll create a class file (named Root.cs because that's the default when processing Root.xsd) that contains objects you can easily plug into .Net to serialize and deserialize the XML. Note that I specified the namespace the classes will have ("/n:myProject.Xml"). I prefer to control that namespace, but defaults are usually fine for the rest. Further, the tool creates partial classes, so if you want custom property accessors, you're fine to add them in a separate file that won't get creamed if you need to run the tool again.
Another tip, I create a text file in my project with the name "<xsd name> xsd Command Line.txt". That way I just have to paste that into the VS command line and don't have to remember everything else I used.
As soon as you go off the beaten track serialisation is a huge PIA.
You need something to mark up as map, probably OtherData as well, so that means you need a class or struct that corresponds to the node.
e.g. WaypointProejctdata as a class Map, that has properties filename, width and height.
My general rule of thumb, is as soon as I have to start messing with my objects to get the xml, serialisation gets binned,and I add an interface that takes an XmlReader or Writer and implement it.
Tryng to get serialisation to do what you want is usually way more code and far less comprehensible than doing that.
Related
I need to give support for an old project and i dont have much experince with xsd .
Project run xsd.exe on DataSet.xsd file and generate DataSet.cs file which include genarated c# classes.
I need a flag or attribute on properties of genarated classes .I add a new attribute to xsd file with name "intradaySupport" .And i try both as attribute and annotation as in figure below but generated class in DataSet.cs have noting with "intradaySupport".
How should i update my DataSet.xsd file so final genarated classes have marked with a flag or attribute.
Full of content atached on link just put content to files stated and run xsd
CDBDataSet.xsd
https://pastebin.com/e0DPAxgb
CustomFields.xsd
https://pastebin.com/ScS3whrY
enter code here
If you want to create an attribute in your xsd you have to specify a <xs:attribute name="intradaySupport" type="xs:boolean" use="required"/> in your xsd. It should be located just after the xs:sequence:
<xs:element name="AccountType">
<xs:complexType>
<xs:sequence>
<xs:element name="Account_x0020_type" type="string12" minOccurs="0" />
<xs:element name="Description" type="string40" minOccurs="0" >
...
<xs:element name="Not_x0020_used" type="string1" minOccurs="0" />
</xs:sequence>
<xs:attribute name="intradaySupport" type="xs:boolean" use="required"/>
</xs:complexType>
</xs:element>
...
using xsd.exe /dataset it will generate a boolean column columnintradaySupport in your data set:
[global::System.Serializable()]
[global::System.Xml.Serialization.XmlSchemaProviderAttribute("GetTypedTableSchema")]
public partial class AccountTypeDataTable : global::System.Data.DataTable, global::System.Collections.IEnumerable {
private global::System.Data.DataColumn columnintradaySupport;
private global::System.Data.DataColumn columnAccount_type;
...
}
I have searched for a bit now, but i'm not able to find a way to autogenerate data from a XML Schema programmatically. Lets say I have this XML schema:
<xs:element xmlns:xs="http://www.w3.org/2001/XMLSchema" name ="Persons">
<xs:complexType>
<xs:sequence>
<xs:element name="Person">
<xs:complexType>
<xs:sequence>
<xs:element name="FirstName" type="xs:string" />
<xs:element name="LastName" type="xs:string" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
I am able to create a XML from this using the VS function "Generate Sample XML"
Is there a way to do this programmatically?
Edit: To specify. I do not want to create all the objects and insert data programmatically myself. I would like for it to create the objects and attributes automatically just like the "Generate Sample XML" in VS. The reason for this is that i would like to change the XSD without having to do anything about xml sample generation.
after doing some searching. I have found a project that have implemented a xml sample generator. I created a test solution and imported the classes. Then i deleted the XmlGen.cs file and created my own main method. The output will be based on the root element.
public static void Main(string[] args)
{
using (var stream = new MemoryStream(File.ReadAllBytes("schema.xsd")))
{
var schema = XmlSchema.Read(XmlReader.Create(stream ), null);
var gen = new XmlSampleGenerator(schema, new XmlQualifiedName("rootElement"));
gen.WriteXml(XmlWriter.Create(#"c:\temp\autogen.xml"));
Console.WriteLine("Autogenerated file is here : c:\temp\autogen.xml");
}
}
You can write simple function for put 1 row into your data table and after that execute DataTable.WriteXml(string filePath)
Somethig like that:
xmlschema1 schema=new xmlschema1();
//put some test data in table
schema.Persons.AddPersonsRow(...some params);
//generate xml
schema.Persons.WriteXml(filePath);
I have one XML File:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<NewDataSet>
<xs:schema id="NewDataSet" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" <xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xs:element name="NewDataSet" msdata:IsDataSet="true" msdata:UseCurrentLocale="true">
<xs:complexType>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="UsersPreferenceSettingsDT">
<xs:complexType>
<xs:sequence>
<xs:element name="USERPROFILEID" type="xs:int" minOccurs="0" />
<xs:element name="SCREENAMEID" type="xs:int" minOccurs="0" />
<xs:element name="FREEZEDCOLUMNID" type="xs:string" minOccurs="0" />
<xs:element name="ORDEROFCOLUMNS" type="xs:string" minOccurs="0" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:choice>
</xs:complexType>
</xs:element>
</xs:schema>
<UsersPreferenceSettingsDT>
<USERPROFILEID>219</USERPROFILEID>
<SCREENAMEID>1</SCREENAMEID>
<FREEZEDCOLUMNID>2|3|4|5</FREEZEDCOLUMNID>
<ORDEROFCOLUMNS>1</ORDEROFCOLUMNS>
</UsersPreferenceSettingsDT>
<UsersPreferenceSettingsDT>
<USERPROFILEID>123</USERPROFILEID>
<SCREENAMEID></SCREENAMEID>
<FREEZEDCOLUMNID>s|s|s</FREEZEDCOLUMNID>
<ORDEROFCOLUMNS></ORDEROFCOLUMNS>
</UsersPreferenceSettingsDT>
</NewDataSet>
In that i have to update the XML Element (i,e USERPROFILeID =219, USERPROFILEID is Unique). At same time another user also update the same XML file but different XML element (i,e USERPROFILEID=123). How can the XML reflect the both users XML update(Like SQL or ORACLE DB Update). I used the following code to update.
userPreferenceSettingsDS.WriteXml(userPreferenceSettingsXMLFilePath, XmlWriteMode.WriteSchema);
but it reflect the last user profile update Value only , while multiple user simultaneously update the XML. Anyone guide me to come out of these issue?
Well, being that we are talking about an xml file and not a database you will not be able to get the "latest" version of the data unless you always load from file and not from memory but even then you expose yourself to concurrency issues.
The best choice here would be to switch to a database or maybe implement something like a Singleton Repository that would serve all users and that way you will always return the latest values of the data in the xml.
And it would fix your other issue, when you write to file you will write the "merged" version of the data that will contain changes from multiple users.
Resources for XML as database:
http://social.msdn.microsoft.com/Forums/en/sqldataaccess/thread/346a2816-faae-461d-aaa8-235d9c0dd55c
XML Database in C#.net
http://bytes.com/topic/c-sharp/answers/460625-c-pure-xml-database
Or use a single profile file per user.
You would need to ensure that only one save operation can happen at a time.
To do this, you need to provide a static (shared) class that reads from and writes to the file. The fancy new buzzword for this is a Singleton instance, but it's simply the following:
public static class MyXMLAccessor
{
public static void saveTheData()
{
//this method would write the data to the XML file and save it.
}
public static string ReadTheData()
{
//to read the data from the file
}
}
This way, you're guaranteed that the result of one operation is saved before the next operation is started.
A more complex solution would be to read the xml into an application-scope dataset so that it can act like a database. You would then periodically write the contents of the dataset back to a file using the DataSet.ToXML() method.
It sounds to me as if you should be using an XML database. The sooner you make the move, the more benefit you will get from it, and the less you will spend on creating your own inadequate home-grown solution.
I have searched for a bit now, but i'm not able to find a way to autogenerate data from a XML Schema programmatically. Lets say I have this XML schema:
<xs:element xmlns:xs="http://www.w3.org/2001/XMLSchema" name ="Persons">
<xs:complexType>
<xs:sequence>
<xs:element name="Person">
<xs:complexType>
<xs:sequence>
<xs:element name="FirstName" type="xs:string" />
<xs:element name="LastName" type="xs:string" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
I am able to create a XML from this using the VS function "Generate Sample XML"
Is there a way to do this programmatically?
Edit: To specify. I do not want to create all the objects and insert data programmatically myself. I would like for it to create the objects and attributes automatically just like the "Generate Sample XML" in VS. The reason for this is that i would like to change the XSD without having to do anything about xml sample generation.
after doing some searching. I have found a project that have implemented a xml sample generator. I created a test solution and imported the classes. Then i deleted the XmlGen.cs file and created my own main method. The output will be based on the root element.
public static void Main(string[] args)
{
using (var stream = new MemoryStream(File.ReadAllBytes("schema.xsd")))
{
var schema = XmlSchema.Read(XmlReader.Create(stream ), null);
var gen = new XmlSampleGenerator(schema, new XmlQualifiedName("rootElement"));
gen.WriteXml(XmlWriter.Create(#"c:\temp\autogen.xml"));
Console.WriteLine("Autogenerated file is here : c:\temp\autogen.xml");
}
}
You can write simple function for put 1 row into your data table and after that execute DataTable.WriteXml(string filePath)
Somethig like that:
xmlschema1 schema=new xmlschema1();
//put some test data in table
schema.Persons.AddPersonsRow(...some params);
//generate xml
schema.Persons.WriteXml(filePath);
In our project we are using Xsd2Code to generate c# code.
The XSD I am trying to adjust looks like this:
<?xml version="1.0"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="File">
<xs:complexType>
<xs:sequence>
<xs:element name="Version" type="xs:string" />
<xs:element name="Language" type="xs:string" />
<xs:element name="Component" type="xs:anyType" minOccurs="1" maxOccurs="unbounded" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
Within our project we now defined an abstract class Version. Is it possible to use our own class Version instead of string as the type of the element Version within the Xsd?
We would like to generate as many classes as possible using Xsd2Code. That's why I want to avoid to also having to create the File class by hand.
Thanks in advance for any help.
You can create a new abstract class Version in xsd scheme and then make your defined Version class as partial and put it to the same namespace as the generated classes. By default all your generated classes must be partial.