Create XSD for hierarchical data - c#

I want to export objects to XML and create XSD. following are the base classes
class Owner
{
private int id;
private string name;
private List<Car> cars;
private int age;
}
class CarTemplate
{
private int drive;
private double engineCc;
}
class Car
{
private int id;
private string name;
private double cost;
private CarTemplate template;
}
Following is the modified structure that I want to export to XML and create XSD out of it
class ExportedOwner
{
private int id;
private string name;
private List<Car> cars;
}
class ExportedCar
{
private string name;
private double cost;
private CarTemplate template;
}
The structure of XML should be in following format
<?xml version="1.0" encoding="utf-8" ?>
<Owner>
<id>1</id>
<Name>John</Name>
<Age>49</Age>
<Cars>
<Car>
<id>1</id>
<Name>Merc Class C</Name>
<CarTemplate>
<drive>2</drive>
<engineCc>2500</engineCc>
</CarTemplate>
</Car>
<Car>
<id>2</id>
<Name>Merc Class M</Name>
<CarTemplate>
<drive>4</drive>
<engineCc>2900</engineCc>
</CarTemplate>
</Car>
</Cars>
</Owner>
How can I create XSD for the XML format. I want XSD for importing the XML file back into the appication.

Use XSD.EXE for generating your XSD: it's a standard .NET Framework tool. See http://msdn.microsoft.com/en-us/library/x6c1kb0s(v=vs.110).aspx to see how it is used.

There's 2 distinct questions to answer here...
First, how to export your objects to an XML file. There are many ways to do this in .NET. The two most common (XmlSerializer and DataContractSerializer) are compared side by side in this blog post.
The second part of your question is a little more broad. Generating an xsd schema from an existing XML file is not an exact science, but there are tools out there that can infer (or guess) a schema from an XML file. There are various little tools which can do this for you - it might be worth trying a few of them and seeing which one gives you best results. As always, google is your good friend :)

You can use the standard XML serializer for this (with xsd.exe). You would need to use the KnownTypeAttribute on the base class in order for the deserialized to know how to rehydrate the structure.
http://msdn.microsoft.com/en-us/library/system.runtime.serialization.knowntypeattribute.aspx

If you want to generate the XML out of your classes dynamically, you could use System.Reflection
For example:
Type type = typeof(ExportedOwner);
var propertyinfos = type.GetProperties(); // gives you a list of all properties of ExportedOwner
And then generate your XML according to your specification with your favourite XMl Writer.

Related

Deserialize using DataContractSerializer

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

Deserialize XML Array Where Root is Array and Elements Dont Follow Conventions

The XML I am getting is provided by an outside source so I don't have the ability to easily reformat it. I would like to use xml attributes on my entities instead of having to write a linq query that knows how the XML and entity is formatted. Here is an example:
<?xml version="1.0"?>
<TERMS>
<TERM>
<ID>2013-2</ID>
<DESC>Spring 2013</DESC>
</TERM>
<TERM>
<ID>2013-3</ID>
<DESC>Summer 2013 Jun&Jul</DESC>
</TERM>
</TERMS>
I know the the XMLSerializer expects ArrayOfTerm instead of TERMS for example, but that I can tweak my entity to use a different element name with the xml attributes such as this:
public class TermData
{
[XmlArray("TERMS")]
[XmlArrayItem("TERM")]
public List<Term> terms;
}
public class Term
{
[XmlElement("ID")]
public string id;
[XmlElement("DESC")]
public string desc;
}
and I am deserializing the data like so:
TermData data;
XmlSerializer serializer = new XmlSerializer(typeof(TermData));
using (StringReader reader = new StringReader(xml))
{
data = (TermData)serializer.Deserialize(reader);
}
return View(data.terms);
The problem I am facing is that TERMS is the root and the array itself. If the XML were to have a root element that was not the array, I could edit my TermData class like so and it would deserialize correctly (already tested).
[XmlRoot("ROOT")]
public class TermData
{
[XmlArray("TERMS")]
[XmlArrayItem("TERM")]
public List<Term> terms;
}
Note that using TERMS as the XMLRoot does not work. Right now, my code is throwing
InvalidOperationException: There is an error in XML document (2,2).
InnerException: "<TERMS xmlns=" was not expected.
This would lead me to believe that the XML is not formatted correctly, but from my understanding the example I gave is perfectly valid XML.
This would all be trivial if I could edit the source xml, but there could be tons of other responses like this and I need to be able to flex for whatever I might get. What I'm trying to confirm is whether or not the XMLSerializer can support this type of XML structure. I've tested just about everything and can't get it deserialize without editing the XML. It would also be convenient if I didn't have to define a wrapper class (TermData) to hold the list, but this seems to only work if the xml follows the naming conventions for the serializer (ArrayOfTerm, etc).
Maybe you can try :
[XmlRoot("TERMS")]
public class TermData
{
public TermData()
{
terms = new List<Term>();
}
[XmlElement("TERM")]
public List<Term> terms{get;set;}
}
public class Term
{
[XmlElement("ID")]
public string id{get;set;}
[XmlElement("DESC")]
public string desc{get;set;}
}
Hope this will help,

XmlSerializer differs between .NET 3.5 & CF.NET 3.5

I've a library that turns under CF.NET & .NET but serialization differs between both. As a result, a XML file generated under CF.NET isn't readable under .NET, and that is a big problem for me !
Here the code sample :
[Serializable, XmlRoot("config")]
public sealed class RemoteHost : IEquatable<RemoteHost>
{
// ...
}
public class Program
{
public static void Main()
{
RemoteHost host = new RemoteHost("A");
List<RemoteHost> hosts = new List<RemoteHost>();
hosts.Add(host);
XmlSerializer ser = new XmlSerializer(typeof(List<RemoteHost>));
ser.Serialize(Console.Out, hosts);
}
}
CF.NET xml :
<?xml version="1.0"?>
<ArrayOfConfig xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<config Name="A">
</config>
</ArrayOfConfig>
.NET xml
<?xml version="1.0" encoding="ibm850"?>
<ArrayOfRemoteHost xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<RemoteHost Name="A">
</RemoteHost>
</ArrayOfRemoteHost>
How can I modify my program in order to generate the same XML ?
It looks like a bug processing the root name, indeed. As a workaround: take control of the root manually:
[XmlRoot("foo")]
public class MyRoot {
[XmlElement("bar")]
public List<RemoteHost> Hosts {get;set;}
}
This should serialize as
<foo><bar>...</bar>...</foo>
on either platform. Substitute foo and bar for your preferred names.
(personally, I'd be using binary output, though ;p)

Why does the OnDeserialization not fire for XML Deserialization?

I have a problem which I have been bashing my head against for the better part of three hours. I am almost certain that I've missed something blindingly obvious...
I have a simple XML file:
<?xml version="1.0" encoding="utf-8"?>
<WeightStore xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Records>
<Record actual="150" date="2010-05-01T00:00:00" />
<Record actual="155" date="2010-05-02T00:00:00" />
</Records>
</WeightStore>
I have a simple class structure:
[Serializable]
public class Record
{
[XmlAttribute("actual")] public double weight { get; set; }
[XmlAttribute("date")] public DateTime date { get; set; }
[XmlIgnore] public double trend { get; set; }
}
[Serializable]
[XmlRoot("WeightStore")]
public class SimpleWeightStore
{
[XmlArrayAttribute("Records")]
private List<Record> records = new List<Record>();
public List<Record> Records { get { return records; } }
[OnDeserialized()]
public void OnDeserialized_Method(StreamingContext context)
{
// This code never gets called
Console.WriteLine("OnDeserialized");
}
}
I am using these in both calling code and in the class files:
using System.Xml.Serialization;
using System.Runtime.Serialization;
I have some calling code:
SimpleWeightStore weight_store_reload = new SimpleWeightStore();
TextReader reader = new StringReader(xml);
XmlSerializer deserializer = new XmlSerializer(weight_store.GetType());
weight_store_reload = (SimpleWeightStore)deserializer.Deserialize(reader);
The problem is that I am expecting OnDeserialized_Method to get called, and it isn't.
I suspect it might have something to do with the fact that it's XML deserialization rather than Runtime deserialization, and perhaps I am using the wrong attribute name, but I can't find out what it might be.
Any ideas, folks?
There's no equivalent of OnDeserialized for XML deserialization.
See this post for workarounds: How do you find out when you've been loaded via XML Serialization?
The only way you could do that in a graceful way is to manually implement IXmlSerializable, which is not fun. Simply; XmlSerializer doesn't support serialization callbacks.
Sometimes, though, you can switch to DataContractSerializer, which still offers xml capabilities but which does support serialization callbacks. Unfortunately the xml options are limited - it won't work for you xml structure, since that uses attributes (DataContractSerializer only supports elements).
You might also look at the comments on this answer, which discusses the points from this.

XML Deserialization with C# .NET 3.5

I Have this XML File
<?xml version="1.0" standalone="yes"?>
<Root>
<Object>
<referenceName>People</referenceName>
<query>select * from people</query>
</Object>
<Object>
<referenceName>Countries</referenceName>
<query>select * from countries</query>
</Object>
</Root>
I need to convert into an object with C#.
I got confused how to do it.
Kindly note that I can have alot of objects in the xml file.
I know that i have to use an [XMLArray......
Thanks
The simplest trick here is at the VS command line:
xsd example.xml
xsd example.xsd /classes
Et voila; one example.cs file with example C# that shows how to get that xml from .NET objects via XmlSerializer.
In this case, I expect the following would work:
public class Root
{
[XmlElement("Object")]
public List<SomeOtherObject> Objects { get; set; }
}
public class SomeOtherObject
{
[XmlElement("referenceName")]
public string Name { get; set; }
[XmlElement("query")]
public string Query { get; set; }
}
update: validated it; yup, it works...
XmlSerializer ser = new XmlSerializer(typeof(Root));
using (XmlReader reader = XmlReader.Create(
new StringReader(xml)))
{
var obj = (Root)ser.Deserialize(reader);
// use obj
}
Use the xsd.exe tool to generate an initial set of classes to start with. Once you have those, tweak if needed (post the generated classes) and use System.Xml.Serialization.XmlSerializer to deserialize back into the runtime object.

Categories

Resources