Recursive XSD Help - c#

i'm trying to learn a little bit XSD and I'm trying to create a XSD for this xml:
<Document>
<TextBox Name="Username" />
<TextBox Name="Password" />
</Document>
... so there's an element, which is an abstract complex type. Every element have elements and so on. Document and TextBox are extending Element.
I trid this:
<?xml version="1.0" encoding="utf-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="Document">
<xs:complexType>
<xs:complexContent>
<xs:extension base="Element">
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:element>
<xs:complexType name="Element" abstract="true">
<xs:sequence minOccurs="0" maxOccurs="unbounded">
<xs:element name="Element" type="Element"></xs:element>
</xs:sequence>
</xs:complexType>
<xs:complexType name="TextBox">
<xs:complexContent>
<xs:extension base="Element">
<xs:attribute name="Name" type="xs:string" />
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:schema>
I compiled it to C# with Xsd2Code, and now I try to deserialize it:
var serializer = new XmlSerializer(typeof(Document));
var document = (Document)serializer.Deserialize(new FileStream("Document1.xml", FileMode.Open));
foreach (var element in document.Element1)
{
Console.WriteLine(((TextBox)element).Name);
}
Console.ReadLine();
and it dosen't print anything. When I try to serialize it like so:
var serializer = new XmlSerializer(typeof(Document));
var document = new Document();
document.Element1 = new List<Element>();
document.Element1.Add(new TextBox()
{
Name = "abc"
});
serializer.Serialize(new FileStream("d.xml", FileMode.Create), document);
...the output is:
<?xml version="1.0"?>
<Document xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Element1>
<Element xsi:type="TextBox">
<Element1 />
<Name>abc</Name>
</Element>
</Element1>
</Document>
When it should be something like:
<?xml version="1.0"?>
<Document xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<TextBox Name="abc" />
</Document>
Any ideas how to fix the xsd or another code generator?
Thanks.

In the XSD code, look at the line where you declare an element named "Element". This element should be named as such <xs:element name="TextBox" /> if you want to use <TextBox/> in your XML files.
With the above change implemented, you could run xsd.exe /c YourFile.xsd to generate a C# class from the XSD. Include the generated file in your C# project and you'll be able to use it like this:
Document d = new Document();
d.TextBox = new TextBox[]
{
new TextBox() { Name = "Username" },
new TextBox() { Name = "Password" },
};
XmlSerializer ser = new XmlSerializer(typeof(Document));
ser.Serialize(Console.Out, d);
You'll also be able to deserialize the XML back into C# objects.
<Document xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<TextBox xsi:type="TextBox" Name="Username" />
<TextBox xsi:type="TextBox" Name="Password" />
</Document>
Note the xsi:type attribute - because you declared your TextBox element to be of type Element you will have to provide which concrete type implementation to use when deserializing one of these elements. Out of curiosity I tried changing the XSD type of the element named TextBox to the TextBox type, but xsd.exe threw a StackOverflowException. I chuckled. It probably to do with the recursive type relationship between Element and TextBox, but maybe a different tool would handle it differently?

Related

How to validate Xml in that specific way

My problem is that i dont know how to properly write a xsd that allows me to validate xml in such way :
i need to have few necessary nodes (in any order) and to allow any other nodes to be in root
so for example i need to validate such xml, with 2 necessary nodes:
<root>
<necessary1/>
<someRandomNode1/>
<necessary2/>
<someRandomNode2/>
<someRandomNode3/>
</root>
but this has to be in any order and <xs:any/> is probably not what im looking for.
edit:
this 'someRandomNodeX' is not name of node, it can be everything. Number of this unscpecified nodes is unknown too.
There is a solution if the required elements and the non-required elements can be made in different namespaces. It requires XML Schema 1.1.
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema
xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"
xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning" vc:minVersion="1.1"
xmlns:namespace="http://www.example.com/"
targetNamespace="http://www.example.com/">
<xs:element name="root">
<xs:complexType>
<xs:choice maxOccurs="unbounded">
<xs:element name="necessary1" type="xs:string"/>
<xs:element name="necessary2" type="xs:string"/>
<xs:any namespace="##other" processContents="lax"/>
<xs:any namespace="##local" processContents="lax"/>
</xs:choice>
<xs:assert test="exactly-one(namespace:necessary1) and exactly-one(namespace:necessary2)"/>
</xs:complexType>
</xs:element>
</xs:schema>
This validates:
<?xml version="1.0" encoding="UTF-8"?>
<namespace:root
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.example.com test.xsd"
xmlns:namespace="http://www.example.com/">
<namespace:necessary1/>
<someRandomNode1/>
<namespace:necessary2/>
<someRandomNode2/>
<someRandomNode3/>
</namespace:root>

Generating XML Documents from XML Schemas

I'm using this xml sample generator to create a sample of the following XSD files:
Main XSD:
<?xml version="1.0" ?>
<xs:schema xmlns:pur="http://NamespaceTest.com/Purchase"
xmlns:cmn="http://NamespaceTest.com/CommonTypes"
xmlns:cust="http://NamespaceTest.com/CustomerTypes"
targetNamespace="http://NamespaceTest.com/Purchase"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified">
<xs:import schemaLocation="test2.xsd"
namespace="http://NamespaceTest.com/CommonTypes" />
<xs:import schemaLocation="test.xsd"
namespace="http://NamespaceTest.com/CustomerTypes" />
<xs:element name="CustomerDetails" type="cust:CustomerType" />
</xs:schema>
Customer XSD:
<?xml version="1.0" ?>
<xs:schema xmlns:cmn="http://NamespaceTest.com/CommonTypes"
targetNamespace="http://NamespaceTest.com/CustomerTypes"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified">
<xs:import schemaLocation="test2.xsd"
namespace="http://NamespaceTest.com/CommonTypes" />
<xs:complexType name="CustomerType">
<xs:sequence>
<xs:element name="Name" type="xs:string" />
<xs:element name="DeliveryAddress" type="cmn:AddressType" />
<xs:element name="BillingAddress" type="cmn:AddressType" />
</xs:sequence>
</xs:complexType>
</xs:schema>
Common Types XSD:
<?xml version="1.0" ?>
<xs:schema targetNamespace="http://NamespaceTest.com/CommonTypes"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified">
<xs:complexType name="AddressType">
<xs:sequence>
<xs:element name="Line1" type="xs:string" />
<xs:element name="Line2" type="xs:string" />
</xs:sequence>
</xs:complexType>
</xs:schema>
The result of the following commands is a xml with a namespace prefix I don't expected, namely xmlns="http://NamespaceTest.com/CustomerTypes instead of xmlns="http://NamespaceTest.com/CommenTypes for the delivery address and billing address:
StringBuilder builder = new StringBuilder();
XmlWriterSettings settings = new XmlWriterSettings { Indent = true };
XmlWriter writer = XmlWriter.Create(builder, settings);
XmlQualifiedName qname = new XmlQualifiedName("CustomerDetails", #"http://www.test.de");
XmlSampleGenerator generator = new XmlSampleGenerator(#"C:\Users\test.xsd", qname);
generator.WriteXml(writer);
Console.WriteLine(builder.ToString());
Result xml:
<?xml version="1.0" encoding="utf-16"?>
<CustomerDetails xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://NamespaceTest.com/Purchase">
<Name xmlns="http://NamespaceTest.com/CustomerTypes">Name1</Name>
<DeliveryAddress xmlns="http://NamespaceTest.com/CustomerTypes">
<Line1 xmlns="http://NamespaceTest.com/CommonTypes">Line11</Line1>
<Line2 xmlns="http://NamespaceTest.com/CommonTypes">Line21</Line2>
</DeliveryAddress>
<BillingAddress xmlns="http://NamespaceTest.com/CustomerTypes">
<Line1 xmlns="http://NamespaceTest.com/CommonTypes">Line11</Line1>
<Line2 xmlns="http://NamespaceTest.com/CommonTypes">Line21</Line2>
</BillingAddress>
</CustomerDetails>

How to create XSD for the given XML?

For the below XML, I need to generate XSD created but getting an error
The 'NewDataSet' element is not declared
<NewDataSet>
<Table>
<SITE>VMD</SITE>
<TANK>65-12-392</TANK>
<SERVICE>HZLPG</SERVICE>
<IP21TAG>BC-BBH-OS-4LI21392</IP21TAG>
</Table>
</NewDataSet>
XSD:
<?xml version="1.0"?>
<xs:schema xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="PAS">
<xs:complexType>
<xs:sequence>
<xs:element name="Records">
<xs:complexType>
<xs:sequence>
<xs:element name="Site" type="xs:string" />
<xs:element name="Plant" type="xs:string" />
<xs:element name="Tank" type="xs:string" />
<xs:element name="Service" type="xs:string" />
<xs:element name="IP21Tag" type="xs:string" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
Can anyone please help how to declare 'NewDataSet' element?
Thanks in advance.
You can generate an XSD from example XML using the xsd.exe supplied from Microsoft. You will only generate an XSD which matches your example so you would need an example to contain all the cases that you wished to parse, or you would have to further edit the XSD to include those.
Anyway this is a good way to get started. MSDN Docs on XSD.exe
Once you have a XSD file you might want to use a tool like XSD2Code which will generate all the code you need to read the XML and turn it into a set of c# objects in memory.
(This assumes you can read all your XML into memory in one go. Otherwise you will need to read your XML using an event SAX type approach.)
to start with, the root element 'NewData' is not declared in your XSD.
I would suggest you take a look at this first: http://www.w3schools.com/schema/schema_example.asp
XmlSchemaInference Class can be used for coverting xml to xsd like:
XmlReader reader = XmlReader.Create ( "contosoBooks.xml" );
XmlSchemaSet schemaSet = new XmlSchemaSet ( );
XmlSchemaInference schema = new XmlSchemaInference ( );
schemaschemaSet = schema.InferSchema ( reader );
foreach ( XmlSchema s in schemaSet.Schemas ( ) )
{
s.Write ( Console.Out );
}
http://msdn.microsoft.com/en-us/library/system.xml.schema.xmlschemainference.aspx
Second way,
Here is example:
Process p = new Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = "Xsd.exe";
p.StartInfo.Arguments = "C:\\config.xml /outputdir:C:\\Temp";
p.Start();
string output = p.StandardOutput.ReadToEnd();
p.WaitForExit();
Console.WriteLine("OUTPUT FROM XSD.EXE : " + output);
This will create config.xsd file from config.xml.
All credit goes to people who answered it on msdn forums.

C# XmlDocument is not being updated with respective XSD default attributes

Here is an example from a xsd and a xml
XML
<?xml version="1.0" encoding="UTF-8"?>
<config test2="true">
</config>
XSD
<?xml version="1.0" encoding="utf-8"?>
<xs:schema targetNamespace="rules.xsd" xmlns="rules.xsd" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msprop="urn:schemas-microsoft-com:xml-msprop">
<xs:element name="config">
<xs:complexType>
<xs:attribute name="test1" type="xs:boolean" default="false" />
<xs:attribute name="test2" type="xs:string" default="mary123" />
</xs:complexType>
</xs:element>
</xs:schema>
I can use this xsd to validate this xml in C# using this block of code
XmlDocument doc = new XmlDocument();
XmlTextReader schemaReader = new XmlTextReader(System.IO.Path.GetFullPath("Mi_XSD_here.xsd"));
XmlSchema schema = XmlSchema.Read(schemaReader, ValidationCallBack);
doc.Schemas.Add(schema);
doc.Load("Mi_XML_here.xml");
doc.Validate(ValidationCallBack);
The problem is: I have two default attributes in xsd, but when i run this code he doesn't insert the attributes in XmlDocument, the result is the same xml that i passed to the system.
The default attributes are not working and i cant figure why they aren't working, i do believe that exists other forms to solve this problem, i did find this but didn't worked
Extensions.Validate Method
obs: ValidationCallBack is some return on error function that i think isn't related to the problems
Your schema's target namespace is rules.xsd so your xml file needs to include that to validate against the schema. Also I'm assuming teste2 is a typo (since that doesn't conform to the schema) and you meant test2:
<config test2="" xmlns="rules.xsd" />
In this case only test1 will get added with a default since test2 is already set to an empty string.

How to add a default namespace to an existing XSD file using C#

I have an existing XSD file and I want to add a namespace. How can I do this using C#?
Starting with something like this:
<?xml version="1.0" encoding="utf-8"?>
<xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="BasicNoiseServiceRequest_201102" nillable="true" type="BasicNoiseServiceRequest_201102" />
<xs:complexType name="BasicNoiseServiceRequest_201102">
<xs:complexContent mixed="false">
<xs:extension base="CrmServiceRequest">
I want to end up with something like this where the xmlns and targetNamespace attributes have been added:
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns="MyNamespace" targetNamespace="MyNamespace"
elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="BasicNoiseServiceRequest_201102" nillable="true" type="BasicNoiseServiceRequest_201102" />
<xs:complexType name="BasicNoiseServiceRequest_201102">
<xs:complexContent mixed="false">
<xs:extension base="CrmServiceRequest">
You can do it using XDocument for parsing and generating xml by generating a new XDocument from the loaded one.
// Parse a string or Load a file
var doc = XDocument.Parse(
#"<?xml version=""1.0"" encoding=""utf-8""?>
<xs:schema elementFormDefault=""qualified"" xmlns:xs=""http://www.w3.org/2001/XMLSchema"">
<xs:element name=""BasicNoiseServiceRequest_201102"" nillable=""true"" type=""BasicNoiseServiceRequest_201102"" />
<xs:complexType name=""BasicNoiseServiceRequest_201102"">
<xs:complexContent mixed=""false"">
<xs:extension base=""CrmServiceRequest"">
...
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:schema>
");
// Get the root of the document
var root = doc.Root;
// Create a new XDocument and add all the required attributes
// Keep all the existing xml file (declaration, child nodes, ...)
doc = new XDocument(doc.Declaration,
new XElement(root.Name, root.Elements(),
root.Attributes().Concat(new[]
{
new XAttribute("xmlns", "MyNamespace"),
new XAttribute("targetNamespace", "MyNamespace")
}))
);

Categories