all groups > dotnet xml > july 2004 >
You're in the

dotnet xml

group:

help with reading xml file


help with reading xml file mycsharpmail NO[at]SPAM yahoo.com
7/29/2004 6:16:16 AM
dotnet xml:
Hello, I am working in .Net C# and have an xml file similar to the one
below. I have tried using a DataSet but get the error "The same table
(Gid) cannot be the child table in two nested relations". The file
has a number of parent nodes at the "<ShipmentHeader>" level, each of
which have a number of child nodes. I will not know ahead of time
which of these parent/child nodes will occur. I have also looked at
the XmlTextReader, but it looks like I would have to evaluate
NodeTypes, ReadInnerXml, etc. Is there an easier way?

<?xml version="1.0" encoding="UTF-8" ?>
<Shipment>
<ShipmentHeader>
<ShipmentGid>
<Gid>
<DomainName>xxxxx</DomainName>
<Xid>xxxxxxx</Xid>
</Gid>
</ShipmentGid>
<ShipmentRefnum>
<ShipmentRefnumQualifierGid>
<Gid>
<Xid>xxxxx</Xid>
</Gid>
</ShipmentRefnumQualifierGid>
<ShipmentRefnumValue>xxxxx</ShipmentRefnumValue>
<ShipmentRefnum>xxxxx</ShipmentRefnum>
<ShipmentRefnumQualifierGid>
<Gid>
<Xid>xxxx</Xid>
</Gid>
</ShipmentRefnumQualifierGid>
<ShipmentRefnumValue>xxxxxx</ShipmentRefnumValue>
</ShipmentRefnum>
</ShipmentHeader>
Re: help with reading xml file Dino Chiesa [Microsoft]
7/29/2004 12:19:43 PM
[quoted text, click to view]
Yes, using XML Serialization. Here's what I would do.

Take your sample XML document, and remove the duplicate element names (Gid
and ShipmentRefnum). Run that through xsd.exe to infer a schema for the
document. This generates an XSD. Now modify the XSD to restore the
original element names. Run the modified XSD through xsd.exe and generate
classes (xsd.exe /c blah.xsd ).

This gives you a starting point.

Build a test driver that
a. De-serializes from the original XML. Does it work?
b. instantiates a new "Shipment" object, and serializes that. Does it
look like your original XML ?

By testing various XML input documents, tweaking the XSD, regenerating the
classes, and so, you can iterate, and you can usually get XML Serialization
to do what you want. For example, xsd.exe often infers a string data type,
when what you want is an int. Or it may infer the wrong minOccurs and
maxOccurs limits. So you can make the appropriate mods in the XSD, then
re-generate, and re-test.

Your situation is a bit special, because you have elements that are
repeated, intermixed. For example, ShipmentRefnum has multiple
ShipmentRefnumQualifierGid child elements, and multiple ShipmentRefnumValue
child elements, but they are intermixed:
<ShipmentRefnum>
<ShipmentRefnumQualifierGid..../>
<ShipmentRefnumValue..../>
<ShipmentRefnum.../>
<ShipmentRefnumQualifierGid.../>
<ShipmentRefnumValue..../>

By default, xsd.exe will infer a schema that employs strongly-typed arrays
for elements that are repeated . So in your case, it looks like this:

public class ShipmentRefNum {
[XmlElement]
public string ShipmentRefnum;

[XmlArray]
[XmlArrayItem("ShipmentRefnumQualifierGid", typeof(GidWrapper))]
public GidWrapper[] ShipmentRefnumQualifierGid;

[XmlElement("ShipmentRefnumValue")]
public string[] ShipmentRefnumValue;
}


When you serialize an instance of this, you will get the array elements in
order, eg
<ShipmentRefnum>
<ShipmentRefnum.../>
<ShipmentRefnumQualifierGid..../>
<ShipmentRefnumQualifierGid.../>
<ShipmentRefnumValue..../>
<ShipmentRefnumValue..../>

which is probably not what you want. Also, I am not sure if
de-serialization from the sample you posted will work properly, either.

To address this, you can modify the generated type for ShipmentRefnum to
contain a single member, an ArrayList. Think of it as a container that can
hold any combination of elements in any order. Then you attribute that
arraylist with XmlElement attributes for each of the types (string, int,
ShipmentRefnumQualifierGid) it will contain. like this:

public class RefnumType {

[XmlElement(ElementName="ShipmentRefnumQualifierGid",Type=typeof(GidWrapper)
)]
[XmlElement(ElementName="ShipmentRefnumValue",Type=typeof(string))]
[XmlElement(ElementName="ShipmentRefnum",Type=typeof(int))]
public ArrayList Items;
}

In XSD, this corresponds to a choice element:
<xs:complexType name="RefnumType">
<xs:sequence>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element minOccurs="1" maxOccurs="1" name="ShipmentRefnum"
type="xs:int" />
<xs:element minOccurs="0" maxOccurs="1"
name="ShipmentRefnumQualifierGid" type="GidWrapper" />
<xs:element minOccurs="0" maxOccurs="1" name="ShipmentRefnumValue"
type="xs:string" />
</xs:choice>
</xs:sequence>
</xs:complexType>


With this structure, you can de-serialize and serialize the XML you posted.
You can also de-serialize and serialize XML that has these elements in
different orders, which may or may not be what you want.

Anyway here's a working example
http://www.winisp.net/cheeso/srcview.aspx?dir=xml-serialization&file=Intermixed.cs

This works with the single XML file you sent; it may not work with all. For
example, you might need to apply the same pattern (using ArrayList) at the
ShipmentHeader level.

NB: This will work only if you have at most ONE element type of each data
type in the arraylist. In other words, you are using the type of the
arraylist item to determine the element name to spit out in XML. If you
have 2 element types that are both strings or both ints, then the XML
Serializer cannot know which element name to emit for that arraylist item of
that type.


-Dino


[quoted text, click to view]

AddThis Social Bookmark Button