XML Serialization in .NET
Total Page:16
File Type:pdf, Size:1020Kb
XMLSerializationin.NET VenkatSubramaniam [email protected] http://www.durasoftcorp.com Abstract XMLSerializationin.NETprovideseaseofdevelopment,convenienceandefficiency. This article discusses the benefits of XML serialization and ways to configure the generatedXMLdocument.Itgoesfurthertodiscusssomeoftheissuesanddeficiencies ofthisprocessaswell.ThisarticleassumesthatthereaderisfamiliarwithXMLformat, XMLSchemaandC#. ProcessingXMLin.NET The.NETframeworkhasanumberofclassestoprocessXMLdocuments.Tostartwith, thefeaturesoftheMSXMLparser,whichwasaCOMcomponent,hasnowbeenmoved intothe.NETframeworkwithmoreefficiency.ThecompleteDOMAPIisimplemented in the System.Xml namespace. XmlDocument is the class that represents a DOM document node and various classes like XmlElement, XmlAttribute, etc., represent the differenttypesofnodesintheDOMAPI.WhileSAXis,sotosay,apulltechnology,a similarbutmoreefficientpushtechnologyisintroducedin.NETthroughtheXmlReader class.TheXmlReaderallowsyoutoprocessanXMLdocumentbyinstructingtheparser toreadandnavigateseriallythroughanXMLdocument.TheXmlReaderprovidesafast, read-only,forward-onlyaccesstoanXMLdocument.WhiletheseclassesandAPIsare significant, our focus in this article is on XML Serialization, and we will not discuss theseclassesfurtherinthisarticle. Aproblemthatwillbenefit WegotourfirstexposuretoXMLSerializationwhenweweredevelopinganASP.NET application.Wewanted togathersomeinformationfromtheuserandkeepitinXML format,sowecouldeasilyapplyastylesheettoitanddisplaythecontentsanytime.The applicationdidnotwarranttheuseofanyDBMS.Thefirstapproachwetookwastostart writingthe XMLdocumentfromtheuserspecifiedinformationusingthestandardfile I/O classes.Further,inordertofetchtheinformationagainforlatermodifications,we had to use an XML parser. Of course, using the parser to process the contents of the XMLdocumentisbetterthanreadingthecontentsbyourselves.However,thefactthat wehadtowriteeachandeverytagoutwasquitebothersome.Wecouldhaveusedthe XMLWriterclassprovidedintheSystem.Xmlnamespacetodothat.But,wouldn’titbe niceifwecansimplytakethedatafromanobjectandwriteoutanXMLdocumentand alsoperformthereverseoperationoftakinganXMLdocumentandconvertingitback intoanobject?PrettysoonwefoundoutthatthisisexactlywhatXMLSerializationdoes. The process of transforming the contents of an object into XML format is called serialization, and the reverse process of transforming an XML document into a .NET objectiscalleddeserialization. Anexample LetustakeaC#classshownbelow(itcouldbeVB.NEToranyother.NETlanguagefor thatmatter): publicclassCar { privateintm_yearOfMake; publicintyearOfMake { get{returnm_yearOfMake;} set{m_yearOfMake=value;} } publicoverridestringToString() { return"Caryear:"+m_yearOfMake; } } AssumethatwewanttoconverttheinformationinanobjectoftheaboveCarclassinto XMLrepresentation.GivenbelowisasamplecodethatwillletuseitherconvertaCar object into an XML representation, or to create a Car object given a valid XML document. classUser { [STAThread] staticvoidMain(string[]args) { if(args.Length!=2) { Console.WriteLine("UsageXMLSerialization [r|s]filename"); Console.WriteLine("rtoread,stosave"); } else { stringfileName=args[1]; if(args[0]=="r") { System.Xml.Serialization.XmlSerializer serializer= new System.Xml.Serialization.XmlSerializer( typeof(Car)); System.IO.FileStreamstream= newSystem.IO.FileStream(fileName, System.IO.FileMode.Open); Carobj=serializer.Deserialize( stream)asCar; Console.WriteLine(obj); } else { Carobj=newCar(); obj.yearOfMake=2002; System.Xml.Serialization.XmlSerializer serializer= new System.Xml.Serialization.XmlSerializer( typeof(Car)); System.IO.FileStreamstream= newSystem.IO.FileStream(fileName, System.IO.FileMode.Create); serializer.Serialize(stream,obj); } } } } Note that we first create an object of XmlSerializer. The XMLSerializer takes an argumentwhichistheTypereflectionmetaobjectoftheCarclass.Wethencalleither the Serialize method or the Deserialize method on it. The Serialize method takes a FileStream and an object of Car as arguments, while the Deserialize method takes the FileStreamandreturnsanobjectofCar. Runningtheprogramas XMLSerializationscar.xml createsanXMLdocumentcar.xmlasshownbelow: <?xmlversion="1.0"?> <Carxmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <yearOfMake>2002</yearOfMake> </Car> NoticethatCaristherootelementnameandtheyearOfMakefieldoftheCarbecamea childelementoftherootelement.Bydefault,eachpublicfieldandpublicpropertyofan objectistransformedintoanXMLelement.WhatifwewanttheyearOfMaketoappear asanattributeandnotasachildelement?Thisisveryeasytoachieve.Let’smodifythe Carclassasfollows: [System.Xml.Serialization.XmlRoot("Automobile")] publicclassCar { privateintm_yearOfMake; [System.Xml.Serialization.XmlAttribute("Year")] publicintyearOfMake { get{returnm_yearOfMake;} set{m_yearOfMake=value;} } publicoverridestringToString() { return"Caryear:"+m_yearOfMake; } } The XmlRoot attribute indicates that the root element’s name should be Automobile insteadofCar.TheXmlAttributeattributeindicatesthatthepublicpropertyyearOfMake shouldappearasanattribute,withnameYear,insteadofappearingasachildelement likeitdidinthepreviouscase. NochangeisrequiredtotheUserclass.Simplyrunningtheprogramagainproducesthe followingcar.xmldocument: <?xmlversion="1.0"?> <Automobilexmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Year="2002"/> Note that the yearOfMake now appears as an attribute and the root element’s name is Automobile - thanks to the attributes that we set on the class Car and its yearOfMake property. Howaboutaggregation? WhatiftheCarhasanaggregationrelationshiptoanEngine?HereistherelatedEngine classandtheCarclassmodifiedtodojustthat: publicclassEngine { privateintm_power; [System.Xml.Serialization.XmlAttribute] publicintPower { get{returnm_power;} set{m_power=value;} } publicoverridestringToString() { return"power"+m_power; } } [System.Xml.Serialization.XmlRoot("Automobile")] publicclassCar { privateintm_yearOfMake; privateEnginem_engine; [System.Xml.Serialization.XmlAttribute("Year")] publicintyearOfMake { get{returnm_yearOfMake;} set{m_yearOfMake=value;} } publicEngineTheEngine { get{returnm_engine;} set{m_engine=value;} } publicCar() { m_yearOfMake=2002; m_engine=newEngine(); m_engine.Power=150; } publicoverridestringToString() { return"Caryear:"+m_yearOfMake+ "withEngine"+m_engine; } } NochangeisagainrequiredtotheUserclass.Simplyrunningtheprogramagaincreates thefollowingcar.xmldocument: <?xmlversion="1.0"?> <Automobilexmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Year="2002"> <TheEnginePower="150"/> </Automobile> Youmaymodifythecar.xmltochangethepoweroftheEngineto200andtheyearof maketo2003andruntheprogramasfollows: XMLSerializationrcar.xml &TDhUeo\uHtDpUutofthepZroLWgrKam(QwJLilQlHbeS:RZHU XMLSchemageneration Atool,xsd.exe,isprovidedwith.NETframeworktogenerateanXMLschemafroma given .NET class. This tool may also be used to generate a .NET class give an XML schema.Let’stryitoutonourCarclassbytypingthefollowingfromaVisualStudio .NETcommandprompt: The xsd.exe was asked to generate an XML schema for the Car class. The generated schemaisshownbelow: <?xmlversion="1.0"encoding="utf-8"?> <xs:schemaelementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:elementname="Automobile"nillable="true" type="Car"/> <xs:complexTypename="Car"> <xs:sequence> <xs:elementminOccurs="0"maxOccurs="1" name="TheEngine"type="Engine"/> </xs:sequence> <xs:attributename="Year"type="xs:int"/> </xs:complexType> <xs:complexTypename="Engine"> <xs:attributename="Power"type="xs:int"/> </xs:complexType> </xs:schema> You may observe from the schema that the format of the generated XML document matcheswiththestructurespecifiedbythisXMLSchema. Thexsd.exealsohasoptionstogeneratea.NETclassgivenanXMLschema.Thiscomes inhandyifyouneedtoreceiveanXMLdocumentfromanotherapplicationandprocess itinyourapplication.InsteadofparsingtheXMLdocumentusingoneoftheAPIslike DOM, you can simply deserialize the XML document into a .NET object. This saves quiteabitofeffortinreceivingandprocessingXMLdocumentsinyourapplication. Whataboutcollections? Let’sextendtheaboveexampletoacollectionofCars.Wewillalsoaddanamespaceto thegeneratedXMLdocument. IntheCarclass,weaddaconstructorasshownbelow: publicCar(intyear) { m_yearOfMake=year; m_engine=newEngine(); m_engine.Power=150; } WethenwriteaShopclassasshownbelow: [System.Xml.Serialization.XmlRoot("AutoShop", Namespace="http://www.auto.com")] publicclassShop { privateCar[]m_cars=newCar[3]; [System.Xml.Serialization.XmlArray("Automobiles"), System.Xml.Serialization.XmlArrayItem(typeof(Car))] publicCar[]cars { get{returnm_cars;} set{m_cars=value;} } } NoticehowwehavesetthenamespaceforthexmldocumentintheXmlRootattribute. Also,wehaveindicatedthatwearedealingwithanarrayandthearrayitemsareoftype CarusingtheXmlArrayandXmlArrayItemattributes. TheUser.csismodifiedtocreateandserializeaShopobjectasfollows: ShopaShop=newShop(); aShop.cars[0]=newCar(2000); aShop.cars[1]=newCar(2001); aShop.cars[2]=newCar(2002); System.Xml.Serialization.XmlSerializerserializer =new System.Xml.Serialization.XmlSerializer( typeof(Shop)); System.IO.FileStreamstream= newSystem.IO.FileStream(fileName,