Any tool that will make a Strongly Typed object from an XSD?

cjard

Well-known member
Joined
Apr 25, 2006
Messages
7,081
Programming Experience
10+
Hi

So, I'm pretty well up on datasets and the dataset generator, but time has come where I'm having to form XML requests and parse responses directly to/from a webserver. I'd like to know, given that I have the XSD for the request/response, whether there is some tool I can run to turn this into a block of code so i can say:

Dim x as New MyXMLRequest
x.Whatever = Value
x.Whatever.OtherTHing = ANotherValue

tcpSocket.Write(x.ToXML())



I've used a tool called XSD.exe to generate a dataset from the XSD, but the XML it forms isnt quite right..
 
Xsd.exe can also output to a serializable class (/c instead of /d) but the output compared to a Dataset.WriteXml should be the same. How is the output wrong? Perhaps the Xsd schema is not correct?
 
DataSets dont quite nest correctly for this application, I think.. They are qite flat in nature (DataSet, nest DataTable, nest columns/values) :

The xml might look like:

VB.NET:
<EXPERIAN>
          <ESERIES>
                    <FORM_ID>B2B</FORM_ID>
          </ESERIES>
          <EXP></EXP>
          <INS1_UNITTEST></INS1_UNITTEST>
          <VER1>
                    <PRODUCT>MFIL</PRODUCT>
                    <VERSION>0101</VERSION>
          </VER1>
          <CDCI>
                    <TRANSACTIONTYPE>01</TRANSACTIONTYPE>
                    <PAYMENTCOLLECTIONTYPE>02</PAYMENTCOLLECTIONTYPE>
                    <CLIENTMEMBER></CLIENTMEMBER>
                    <CERTIFICATEREQUIRED></CERTIFICATEREQUIRED>
                    <DEMONSTRATIONFLAG></DEMONSTRATIONFLAG>
                    <EXPERIANINPUTFLAG></EXPERIANINPUTFLAG>
                    <VEHICLEDESCRIPTIONONLY></VEHICLEDESCRIPTIONONLY>
                    <VRM>CPP197K</VRM>
                    <VALUATIONBUYSELLINDICATOR>B</VALUATIONBUYSELLINDICATOR>
                    <REGISTRATIONDETAILS>1</REGISTRATIONDETAILS>
                    <GLASSVEHICLEVALUES>1</GLASSVEHICLEVALUES>
          </CDCI>
</EXPERIAN>

I've created the XSD for something like this, and I used xsd.exe to turn it into a dataset, but the hierarchy didnt work out properly.. The XML I was working with has more levels of nest.. I'd love to post it, but I dont have any sample XML; I had to scour the web for this example and it only loosely relates to my app. If I used XSD.EXE with the /c parameter, would I get a class that would be used like:

Dim x as New ExperianObject
x.CDCI.VRM = "CPP197K"

x.nest.nest.nest.nest.value = "whatever"

etc..
 
Xsd.exe will make all these first childs that have childnodes to array types (minOccurs="0" maxOccurs="unbounded"), you can change the schema to single item by removing maxOccurs.

CDCI node is also a generated class so it has to be instantiated, you can change the generated Experian class to create instances of child nodes when Experian constructor is invoked. minOccurs/maxOccurs is 1 by default, but neither implicitly or explicitly defined will the generated class create "default instances" of child nodes.

If you follow the two mentioned modifications your code is valid.
 
OK.. so suppose there is a complex type called
ADDRESS

That has several simple type children

Now, experian might send/accept multiple address blocks so having them as an array or list might be handy. Can you tell me if other code translations are performed? For example, if I specify that DayOfBirth is a positive integer minInclusive = 1 maxInclusive = 31, will that be translated into some code?
 
Xsd.exe won't do anything about this when generating the class.

If you want array type to remain then changing it to a List(Of T) would be easier to work with later.
 
Having now generated a load of classes, and instantiated them and set properties to values, how does one turn this into a block of XML?

Maybe I'm missing something, but I'd hoped for the top level EXPERIAN class to have a .ToXMLString() that enumerated all properties etc.. Something I can post to experien's web server using a standard http post request, and have it do soemthing interesting! :)
 
You serialize/deserialize it, here is example to and fro where x is the EXPERIAN instance, stream is any kind of stream:
VB.NET:
Dim ser As New Xml.Serialization.XmlSerializer(x.GetType) 'or GetType(EXPERIAN)
ser.Serialize([I]stream[/I], x)
x = ser.Deserialize([I]stream[/I])
 
Excellent, thanks John!

Last Q:

Here's what I get:
VB.NET:
<?xml version="1.0" encoding="utf-16"?>
<EXPERIAN xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://tempuri.org/EseriesSchema.xsd">
  <ESERIES>
    <FORM_ID>AUTHENTICATE_PLUS</FORM_ID>
  </ESERIES>
  <ADDR>
    <HOUSENUMBER>1</HOUSENUMBER>
    <POSTCODE>N80 1TH</POSTCODE>
  </ADDR>
</EXPERIAN>

Experian's docs say they don't want the header stuff, they jsut want:

VB.NET:
<EXPERIAN>
  <ESERIES>
    <FORM_ID>AUTHENTICATE_PLUS</FORM_ID>
  </ESERIES>
  <ADDR>
    <HOUSENUMBER>1</HOUSENUMBER>
    <POSTCODE>N80 1TH</POSTCODE>
  </ADDR>
</EXPERIAN>
Are we talking simple string manipulation here?

When they send back the response, it wont have typical xml headers either. Will I have to manipualte it in before it will deserialize?

Thanks
 
And the othe question I had, related to something you mentioned..
By default, no instances are made so I should create instances in the constructor of EXPERIAN so that property accesses dont NullRef:

ex.ADDR.HOUSENUMBER = 15


Can I do this in the actual property? If i put a check on the GET and SET sides:

If _ADDR Is Nothing Then _addr = New ADDR()


For every property (If <relevant member> Is Nothing Then <relevant member> = New <RELEVANT MEMBER>), will they all instantiate themselves as they go? Even when deserializing?

Thing is, I dont know which, if any, the user will want to use. On the fly instancing will mean the smallest memory footprint and smallest xml request string is formed. Experian say that some elements should be included even if they are empty so I can use the constructors to set those to empty strings, just so they are included in the request..
 
You can also create default instance with the private field:
VB.NET:
Private aDDRField As [B]New[/B] EXPERIANADDR
This is same as putting that check and instance creation in the Getter what serialization regards (There is no point in doing it in the Setter, that's where something is assigned anyway). It will lead to empty nodes being added to the output, you will get this node:
HTML:
<ADDR />
About the xml header declaration, does it harm when leaving it there, or will the receiver ignore this?

I'm not the only one getting annoyed by serialization leaving the namespace declarations for prefixes that is not used in the output, you can remove them by adding a "null namespace" like this:
VB.NET:
Dim ns As New Xml.Serialization.XmlSerializerNamespaces
ns.Add("", "")
ser.Serialize(stream, x, ns)
Deserializing is no problem when xml header and those namespace definition is absent.
 
Here's the quirky way of also removing the xml declaration, use a temporary MemoryStream which serialized to, load it into XmlDocument, create a XmlWriter with settings specified to OmitXmlDeclaration, then save the XmlDocument through this writer to the final stream:
(x is still the Experian instance, stream still the final output stream)
VB.NET:
        Dim mem As New IO.MemoryStream
        Dim ser As New Xml.Serialization.XmlSerializer(x.GetType)
        Dim ns As New Xml.Serialization.XmlSerializerNamespaces
        ns.Add("", "")
        ser.Serialize(mem, x, ns)
        mem.Position = 0
        Dim xmldoc As New Xml.XmlDocument
        xmldoc.Load(mem)
        mem.Dispose()
        Dim settings As New Xml.XmlWriterSettings
        settings.OmitXmlDeclaration = True
        settings.Indent = True 'default is False, maybe preferred here
        Dim writer As Xml.XmlWriter = Xml.XmlWriter.Create([I]stream[/I], settings)
        xmldoc.Save(writer)
 
You can also create default instance with the private field:
VB.NET:
Private aDDRField As [B]New[/B] EXPERIANADDR
This is same as putting that check and instance creation in the Getter what serialization regards

Do you mean that for:

Private aDDRField As New EXPERIANADDR


aDDRField will remain Nothing until I use it for the first time? if I never use it, then it will not be serialized and represented in the output?



(There is no point in doing it in the Setter, that's where something is assigned anyway).
True

It will lead to empty nodes being added to the output, you will get this node:
HTML:
<ADDR/>

Experian recommend setting some fields to blank if they are not to be used. If we did this for, say, HouseName:

ex.ADDR.HOUSENAME = String.Empty

Which of the following XML will result:
VB.NET:
<EXPERIAN>
  <ADDR>
    <HOUSENAME/>


<EXPERIAN>
  <ADDR>
    <HOUSENAME></HOUSENAME>

I wonder because I'm not sure just how good Experian are on the technical side of thigns. I asked them for an XSD and they said "Unfortunately we do not provide schemas as they have proved unreliable in the past for clients to use to code from"
Instead I have an MS Word document bit a few tables in. I'd have preferred an XSD.

About the xml header declaration, does it harm when leaving it there, or will the receiver ignore this?
I dont know.. they explicitly stated that no header stuff is allowed, so i presumed it would choke the app server.

The other thing that is annoying with this project, is that Experian charge about $50,000 for the system, but dont provide a test version before the go-live date; they just give documents with a spec and say "anything written to that spec, will work" - So I honestly dont know if the receiver will ignore this or not. I have to do all my testing and coding on the go-live date. Without a recent email form Experian I wouldnt have even known the layout of the XML document, because their specs dont mention it anywhere; the specs are just a list of the tags, their content and max length, starting from hierarchy level 2(request) and level 4 (response). I'm not impressed so far.

Thanks for the info on the namespace serialization; I'll get on and have a play
 
Do you mean that for:

Private aDDRField As New EXPERIANADDR


aDDRField will remain Nothing until I use it for the first time? if I never use it, then it will not be serialized and represented in the output?
No, any way you instantiate a property from the class itself, if it's done in class constructor, by setting the corresponding private field, or with the conditional Getter - any way will make the serialization include it because it access the public properties (and fields) to retrieve the data to be serialized, if anything other than Nothing is returned the node is included. So even if you never access that property in code the serializer does and will then create the instance. Only other option is to leave the class without instances and create these in client code when needed.

The short form "<ADDR />" is used for nodes without content.
 
Removing the Xml declaration can be done simpler, I missed that it is possible to serialize directly through the XmlWriter:
VB.NET:
        Dim ser As New Xml.Serialization.XmlSerializer(x.GetType)
        Dim ns As New Xml.Serialization.XmlSerializerNamespaces
        ns.Add("", "")
        Dim settings As New Xml.XmlWriterSettings
        settings.OmitXmlDeclaration = True
        settings.Indent = True 'default is False, maybe preferred here
        Dim writer As Xml.XmlWriter = Xml.XmlWriter.Create([I]stream[/I], settings)
        ser.Serialize(writer, x, ns)
 
Back
Top