<<

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, XMLSchemaand#. 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 niceifwecansimplytakethefromanobjectandwriteoutanXMLdocumentand 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 [|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. createsanXMLdocumentcar.xmlasshownbelow: 2002 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: 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: Youmaymodifythecar.xmltochangethepoweroftheEngineto200andtheyearof maketo2003andruntheprogramasfollows: XMLSerializationrcar.xml T&DhUeo\uHtDpUutofthepZroLgWrKam(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: 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, System.IO.FileMode.Create); serializer.Serialize(stream,aShop); RunningtheXMLSerializationproducesthefollowingcar.xmldocumentnow: What’sthecatch? As we can see, the XML Serialization mechanism in .NET is pretty powerful. The amountofeffortrequiredintransformingbetweena.NETobjectanditscorresponding XMLrepresentationisminimal.Theclassesrelatedtoserializationandprocessinghave beenimplementedaspartofthe.NETclassframeworkwithatutmostefficiency.There is, however, one thing undesirable. You may have already observed it in the above example.Let’sgobacktotheCarclassonceagain.TheCarhasareferencetoEngine.If weserializetheCar,theEngineisserializedalongwithit.However,areferenceoftype Engine,mayrefertoanobjectoftheEngineclass,oranyclassthatisderivedfromthe Engineclass.LetTurboEnginebeaclassthatinheritsfromtheEngineclass. publicclassTurboEngine:Engine { publicTurboEngine(){Power=300;} publicoverridestringToString() { return"TurboEngine"+base.ToString(); } } WenowmodifytheUser.cstouseaTurboEngineforoneoftheCars: ShopaShop=newShop(); aShop.cars[0]=newCar(2000); aShop.cars[1]=newCar(2001); aShop.cars[2]=newCar(2002); aShop.cars[2].TheEngine=newTurboEngine(); System.Xml.Serialization.XmlSerializerserializer =new System.Xml.Serialization.XmlSerializer( typeof(Shop)); System.IO.FileStreamstream= newSystem.IO.FileStream(fileName, System.IO.FileMode.Create); serializer.Serialize(stream,aShop); Let’sruntheprogramagaintogeneratethecar.xml.Thistime,wegetanexception: “UnhandledException:System.InvalidOperationException:Therewasanerrorgener tingtheXMLdocument.--->System.InvalidOperationException:ThetypeXMLSeria ization.TurboEnginewasnotexpected.Usethe XmlIncludeorSoapIncludeattributeto specifytypesthatarenotknownstatically.…” Theserializationprocessdoesnotdealwithinheritancehierarchyinasmoothway.For thistowork,youwillhavetoindicatethattheenginereferencemayrefertoanobjectof EngineorTurboEngineasfollows: publicclassCar { privateintm_yearOfMake; privateEnginem_engine; [System.Xml.Serialization.XmlElement(typeof(Engine)), System.Xml.Serialization.XmlElement( typeof(TurboEngine))] publicEngineTheEngine { get{returnm_engine;} set{m_engine=value;} } … Runningtheprogramagainproducesthefollowingcar.xmldocument: Whiletheabovefixworks,wehavecompletelyviolatedtheOpen-ClosedPrinciple.The codeisnotextensibletoaddingnewtypesofEngine.Ifwedecidetoaddanotherclass whichinheritsfromEngineorTurboEngine,wewillhavetomodifytheCarclass.This, tosaytheleastisundesirable.Thisseemstobetheonlysignificantlimitationandhope thefuturerevisionsoftheXMLserializationmechanismwilladdressthis. Conclusion In this article, we have presented the details of the support provided for XML Serializationin.NET.XMLSerializationallowsustotransformbetweena.NETobject andanXMLrepresentation.ThismakesiteasiertoexchangeXMLdocumentsbetween applications.Afterall,inanobject-orientedapplicationwedealwithobjectsanditmakes alotofsensetobeabletogenerateanobjectfromanXMLrepresentationandviceversa. This is simply achieved in .NET using the xsd.exe tool. Only public fields and public properties are transformed into XML representation. But, the representation can be controlledandonecandecideonthenamesofelements,attributesandalsowhetheran entity should be represented as a child element or as an attribute. Finally, one major limitationseemstobethenon-extensiblenatureofthemechanism. Itdoesnotsupport inherited types automatically and requires type declaration for each type of inherited class. References 1.MSDNhttp://msdn.microsoft.com