Sunday, September 6, 2015

Enjoying Breeze after Refactoring

Recently I have done some refactoring in one of my c# projects. To show the process, I will give example of a class which was a huge *** 1880 line code with a long train of 22 else-if blocks. After refactoring it became 517 line code and much more manageable. Reflection, delegation, generics and lambda are used in this refactoring. By the way, I've omitted considerable amount of code from the example for readability, so these examples are not in workable state. I will not discuss these examples in detail.....he he.... just going to throw some code to you.

Here is the class before refactoring:

 namespace WebIMCP.GeoToolBox.XMLDOs  
 {  
   public class DefaultLayerBuilder  
   {  
     private DefaultLayerBuilder()  
     {  
     }  
     public static DefaultLayerBuilder Instance(string name, string filePrefix, Type layerType)  
     {  
       DefaultLayerBuilder instance = new DefaultLayerBuilder();  
       instance.name = name;  
       instance.filePrefix = filePrefix;  
       instance.layerType = layerType;  
       instance.addScaleRangeThemesToThemeDefination = true;  
       instance.addSelectionThemeToThemeDefination = true;  
       instance.populateThemeDefination = true;  
       instance.enableThematicScheme = false;  
       return instance;  
     }  
     public DefaultLayerBuilder Enable(bool val)  
     {  
       this.enabled = val;  
       return this;  
     }  
     public DefaultLayerBuilder Description(string val)  
     {  
       this.desc = val;  
       return this;  
     }  
     public ConfigurationLayer Build()  
     {  
       ConfigurationLayer cl = new ConfigurationLayer();  
       defaultScaleRangesData = getDefaultScaleRangesData();  
       defaultPointThemeData = getDefaultPointThemeData();  
       defaultPolylineThemeData = getDefaultPolylineThemeData();  
       defaultPolygonThemeData = getDefaultPolygonThemeData();  
       defaultPolylineLayerRoutingData = getDefaultPolylineLayerRoutingData();  
       defaultPolylineDirectionInfoData = getDefaultPolylineDirectionInfoData();  
       
       if (layerType == typeof(ConfigurationLayerPolygonLayer))  
       {  
         cl.PolygonLayer = new ConfigurationLayerPolygonLayer();  
         this.loadBasicInfoInTypedLayer(cl.PolygonLayer);  
         if (this.enableThematicScheme)  
         {  
           cl.PolygonLayer.ThematicScheme = new List<ConfigurationLayerPolygonLayerThematicScheme>();  
           cl.PolygonLayer.ThematicScheme.Add(new ConfigurationLayerPolygonLayerThematicScheme());  
           cl.PolygonLayer.ThematicScheme[0].Name = "PolygonLayer_DEFAULT_THEMATICSCHEME";  
           cl.PolygonLayer.ThematicScheme[0].ThematicScaleRanges =  
             defaultScaleRangesData  
             .Replace("<ThematicScaleRanges>", "<" + typeof(ConfigurationLayerPolygonLayerThematicSchemeThematicScaleRanges).Name + ">")  
             .Replace("</ThematicScaleRanges>", "</" + typeof(ConfigurationLayerPolygonLayerThematicSchemeThematicScaleRanges).Name + ">")  
             .XMLDeSerialize<ConfigurationLayerPolygonLayerThematicSchemeThematicScaleRanges>();  
           cl.PolygonLayer.ThematicScheme[0].SchemeSelectionTheme = cl.PolygonLayer.ThematicScheme[0].Name + "_SelectionTheme";  
         }  
         else  
         {  
           cl.PolygonLayer.Scheme = new List<ConfigurationLayerPolygonLayerScheme>();  
           cl.PolygonLayer.Scheme.Add(new ConfigurationLayerPolygonLayerScheme());  
           cl.PolygonLayer.Scheme[0].Name = "PolygonLayer_DEFAULT_SCHEME";  
           cl.PolygonLayer.Scheme[0].ScaleRanges =  
             defaultScaleRangesData  
             .Replace("<ScaleRanges>", "<" + typeof(ConfigurationLayerPolygonLayerSchemeScaleRanges).Name + ">")  
             .Replace("</ScaleRanges>", "</" + typeof(ConfigurationLayerPolygonLayerSchemeScaleRanges).Name + ">")  
             .XMLDeSerialize<ConfigurationLayerPolygonLayerSchemeScaleRanges>();  
           cl.PolygonLayer.Scheme[0].SchemeSelectionTheme =  
                         cl.PolygonLayer.Scheme[0].Name + "_SelectionTheme";  
         }  
         if (this.populateThemeDefination)  
         {  
           cl.PolygonLayer.ThemeDefinition = new List<ConfigurationLayerPolygonLayerPolygonTheme>();  
           if (this.addScaleRangeThemesToThemeDefination)  
           {  
             ConfigurationLayerPolygonLayerPolygonTheme defaultRangeTheme =  
               defaultPolygonThemeData  
               .Replace("<Theme", "<" + typeof(ConfigurationLayerPolygonLayerPolygonTheme).Name + "")  
               .Replace("</Theme>", "</" + typeof(ConfigurationLayerPolygonLayerPolygonTheme).Name + ">")  
               .Replace("<DisplayProperties", "<" + layerType.Name.Replace("Configuration", "").Replace("Layer", "") + "Properties")  
               .XMLDeSerialize<ConfigurationLayerPolygonLayerPolygonTheme>();  
             defaultRangeTheme.Name = cl.PolygonLayer.Scheme[0].ScaleRanges.DefaultRange.DefaultType.Theme;  
             cl.PolygonLayer.ThemeDefinition.Add(defaultRangeTheme);  
             foreach (ConfigurationLayerPolygonLayerSchemeScaleRangesScaleRange scaleRange  
                     in cl.PolygonLayer.Scheme[0].ScaleRanges.ScaleRange)  
             {  
               ConfigurationLayerPolygonLayerPolygonTheme scaleRangeTheme =  
               defaultPolygonThemeData  
               .Replace("<Theme", "<" + typeof(ConfigurationLayerPolygonLayerPolygonTheme).Name + "")  
               .Replace("</Theme>", "</" + typeof(ConfigurationLayerPolygonLayerPolygonTheme).Name + ">")  
               .Replace("<DisplayProperties", "<" + layerType.Name.Replace("Configuration", "").Replace("Layer", "") + "Properties")  
               .XMLDeSerialize<ConfigurationLayerPolygonLayerPolygonTheme>();  
               scaleRangeTheme.Name = scaleRange.DefaultType.Theme;  
               cl.PolygonLayer.ThemeDefinition.Add(scaleRangeTheme);  
             }  
           }  
           if (this.addSelectionThemeToThemeDefination)  
           {  
             ConfigurationLayerPolygonLayerPolygonTheme selectionTheme =  
               defaultPolygonThemeData  
               .Replace("<Theme", "<" + typeof(ConfigurationLayerPolygonLayerPolygonTheme).Name + "")  
               .Replace("</Theme>", "</" + typeof(ConfigurationLayerPolygonLayerPolygonTheme).Name + ">")  
               .Replace("<DisplayProperties", "<" + layerType.Name.Replace("Configuration", "").Replace("Layer", "") + "Properties")  
               .XMLDeSerialize<ConfigurationLayerPolygonLayerPolygonTheme>();  
             if (this.enableThematicScheme)  
             {  
               selectionTheme.Name = cl.PolygonLayer.ThematicScheme[0].SchemeSelectionTheme;  
             }  
             else  
             {  
               selectionTheme.Name = cl.PolygonLayer.Scheme[0].SchemeSelectionTheme;  
             }  
             cl.PolygonLayer.ThemeDefinition.Add(selectionTheme);  
           }  
         }  
       }  
       else if (layerType == typeof(ConfigurationLayerPolylineLayer))  
       {  
         cl.PolylineLayer = new ConfigurationLayerPolylineLayer();  
         this.loadBasicInfoInTypedLayer(cl.PolylineLayer);  
         cl.PolylineLayer.Scheme = new List<ConfigurationLayerPolylineLayerScheme>();  
         cl.PolylineLayer.Scheme.Add(new ConfigurationLayerPolylineLayerScheme());  
         cl.PolylineLayer.Scheme[0].Name = "PolylineLayer_DEFAULT_SCHEME";  
         cl.PolylineLayer.Scheme[0].ScaleRanges =  
           defaultScaleRangesData  
           .Replace("<ScaleRanges>", "<" + typeof(ConfigurationLayerPolylineLayerSchemeScaleRanges).Name + ">")  
           .Replace("</ScaleRanges>", "</" + typeof(ConfigurationLayerPolylineLayerSchemeScaleRanges).Name + ">")  
           .XMLDeSerialize<ConfigurationLayerPolylineLayerSchemeScaleRanges>();  
         cl.PolylineLayer.Scheme[0].SchemeSelectionTheme =  
                       cl.PolylineLayer.Scheme[0].Name + "_SelectionTheme";  
         if (this.populateThemeDefination)  
         {  
           cl.PolylineLayer.ThemeDefinition = new List<ConfigurationLayerPolylineLayerPolylineTheme>();  
           if (this.addScaleRangeThemesToThemeDefination)  
           {  
             ConfigurationLayerPolylineLayerPolylineTheme defaultRangeTheme =  
               defaultPolylineThemeData  
               .Replace("<Theme", "<" + typeof(ConfigurationLayerPolylineLayerPolylineTheme).Name + "")  
               .Replace("</Theme>", "</" + typeof(ConfigurationLayerPolylineLayerPolylineTheme).Name + ">")  
               .Replace("<DisplayProperties", "<" + layerType.Name.Replace("Configuration", "").Replace("Layer", "") + "Properties")  
               .XMLDeSerialize<ConfigurationLayerPolylineLayerPolylineTheme>();  
             defaultRangeTheme.Name = cl.PolylineLayer.Scheme[0].ScaleRanges.DefaultRange.DefaultType.Theme;  
             cl.PolylineLayer.ThemeDefinition.Add(defaultRangeTheme);  
             foreach (ConfigurationLayerPolylineLayerSchemeScaleRangesScaleRange scaleRange  
                     in cl.PolylineLayer.Scheme[0].ScaleRanges.ScaleRange)  
             {  
               ConfigurationLayerPolylineLayerPolylineTheme scaleRangeTheme =  
               defaultPolylineThemeData  
               .Replace("<Theme", "<" + typeof(ConfigurationLayerPolylineLayerPolylineTheme).Name + "")  
               .Replace("</Theme>", "</" + typeof(ConfigurationLayerPolylineLayerPolylineTheme).Name + ">")  
               .Replace("<DisplayProperties", "<" + layerType.Name.Replace("Configuration", "").Replace("Layer", "") + "Properties")  
               .XMLDeSerialize<ConfigurationLayerPolylineLayerPolylineTheme>();  
               scaleRangeTheme.Name = scaleRange.DefaultType.Theme;  
               cl.PolylineLayer.ThemeDefinition.Add(scaleRangeTheme);  
             }  
           }  
           if (this.addSelectionThemeToThemeDefination)  
           {  
             ConfigurationLayerPolylineLayerPolylineTheme selectionTheme =  
               defaultPolylineThemeData  
               .Replace("<Theme", "<" + typeof(ConfigurationLayerPolylineLayerPolylineTheme).Name + "")  
               .Replace("</Theme>", "</" + typeof(ConfigurationLayerPolylineLayerPolylineTheme).Name + ">")  
               .Replace("<DisplayProperties", "<" + layerType.Name.Replace("Configuration", "").Replace("Layer", "") + "Properties")  
               .XMLDeSerialize<ConfigurationLayerPolylineLayerPolylineTheme>();  
             selectionTheme.Name = cl.PolylineLayer.Scheme[0].SchemeSelectionTheme;  
             cl.PolylineLayer.ThemeDefinition.Add(selectionTheme);  
           }  
           cl.PolylineLayer.DirectionInfo = defaultPolylineDirectionInfoData  
               .XMLDeSerialize<ConfigurationLayerPolylineLayerDirectionInfo>();  
           cl.PolylineLayer.Routing = defaultPolylineLayerRoutingData  
               .XMLDeSerialize<ConfigurationLayerPolylineLayerRouting>();  
         }  
       }  
       else if (layerType == typeof(ConfigurationLayerPointLayer))  
       {  
         cl.PointLayer = new ConfigurationLayerPointLayer();  
         this.loadBasicInfoInTypedLayer(cl.PointLayer);  
         if (this.enableThematicScheme)  
         {  
           cl.PointLayer.ThematicScheme = new List<ConfigurationLayerPointLayerThematicScheme>();  
           cl.PointLayer.ThematicScheme.Add(new ConfigurationLayerPointLayerThematicScheme());  
           cl.PointLayer.ThematicScheme[0].Name = "PointLayer_DEFAULT_THEMATICSCHEME";  
           cl.PointLayer.ThematicScheme[0].ThematicScaleRanges =  
             defaultScaleRangesData  
             .Replace("<ThematicScaleRanges>", "<" + typeof(ConfigurationLayerPointLayerThematicSchemeThematicScaleRanges).Name + ">")  
             .Replace("</ThematicScaleRanges>", "</" + typeof(ConfigurationLayerPointLayerThematicSchemeThematicScaleRanges).Name + ">")  
             .XMLDeSerialize<ConfigurationLayerPointLayerThematicSchemeThematicScaleRanges>();  
           cl.PointLayer.ThematicScheme[0].SchemeSelectionTheme = cl.PointLayer.ThematicScheme[0].Name + "_SelectionTheme";  
         }  
         else  
         {  
           cl.PointLayer.Scheme = new List<ConfigurationLayerPointLayerScheme>();  
           cl.PointLayer.Scheme.Add(new ConfigurationLayerPointLayerScheme());  
           cl.PointLayer.Scheme[0].Name = "PointLayer_DEFAULT_SCHEME";  
           cl.PointLayer.Scheme[0].ScaleRanges =  
             defaultScaleRangesData  
             .Replace("<ScaleRanges>", "<" + typeof(ConfigurationLayerPointLayerSchemeScaleRanges).Name + ">")  
             .Replace("</ScaleRanges>", "</" + typeof(ConfigurationLayerPointLayerSchemeScaleRanges).Name + ">")  
             .XMLDeSerialize<ConfigurationLayerPointLayerSchemeScaleRanges>();  
           cl.PointLayer.Scheme[0].SchemeSelectionTheme =  
                         cl.PointLayer.Scheme[0].Name + "_SelectionTheme";  
         }  
         if (this.populateThemeDefination)  
         {  
           cl.PointLayer.ThemeDefinition = new List<ConfigurationLayerPointLayerPointTheme>();  
           if (this.addScaleRangeThemesToThemeDefination)  
           {  
             ConfigurationLayerPointLayerPointTheme defaultRangeTheme =  
               defaultPointThemeData  
               .Replace("<Theme", "<" + typeof(ConfigurationLayerPointLayerPointTheme).Name + "")  
               .Replace("</Theme>", "</" + typeof(ConfigurationLayerPointLayerPointTheme).Name + ">")  
               .Replace("<DisplayProperties", "<" + layerType.Name.Replace("Configuration", "").Replace("Layer", "") + "Properties")  
               .XMLDeSerialize<ConfigurationLayerPointLayerPointTheme>();  
             defaultRangeTheme.Name = cl.PointLayer.Scheme[0].ScaleRanges.DefaultRange.DefaultType.Theme;  
             cl.PointLayer.ThemeDefinition.Add(defaultRangeTheme);  
             foreach (ConfigurationLayerPointLayerSchemeScaleRangesScaleRange scaleRange  
                     in cl.PointLayer.Scheme[0].ScaleRanges.ScaleRange)  
             {  
               ConfigurationLayerPointLayerPointTheme scaleRangeTheme =  
               defaultPointThemeData  
               .Replace("<Theme", "<" + typeof(ConfigurationLayerPointLayerPointTheme).Name + "")  
               .Replace("</Theme>", "</" + typeof(ConfigurationLayerPointLayerPointTheme).Name + ">")  
               .Replace("<DisplayProperties", "<" + layerType.Name.Replace("Configuration", "").Replace("Layer", "") + "Properties")  
               .XMLDeSerialize<ConfigurationLayerPointLayerPointTheme>();  
               scaleRangeTheme.Name = scaleRange.DefaultType.Theme;  
               cl.PointLayer.ThemeDefinition.Add(scaleRangeTheme);  
             }  
           }  
           if (this.addSelectionThemeToThemeDefination)  
           {  
             ConfigurationLayerPointLayerPointTheme selectionTheme =  
               defaultPointThemeData  
               .Replace("<Theme", "<" + typeof(ConfigurationLayerPointLayerPointTheme).Name + "")  
               .Replace("</Theme>", "</" + typeof(ConfigurationLayerPointLayerPointTheme).Name + ">")  
               .Replace("<DisplayProperties", "<" + layerType.Name.Replace("Configuration", "").Replace("Layer", "") + "Properties")  
               .XMLDeSerialize<ConfigurationLayerPointLayerPointTheme>();  
             if (this.enableThematicScheme)  
             {  
               selectionTheme.Name = cl.PointLayer.ThematicScheme[0].SchemeSelectionTheme;  
             }  
             else  
             {  
               selectionTheme.Name = cl.PointLayer.Scheme[0].SchemeSelectionTheme;  
             }  
             cl.PointLayer.ThemeDefinition.Add(selectionTheme);  
           }  
         }  
       }  
       return cl;  
     }    
   }  
 }  


And here is the same class after refactoring:


 namespace WebIMCP.GeoToolBox  
 {  
   class DefaultLayerBuilder  
   {  
     private DefaultLayerBuilder()  
     {  
     }  
     public static DefaultLayerBuilder Instance(string name, string filePrefix, Type layerType)  
     {  
       DefaultLayerBuilder instance = new DefaultLayerBuilder();  
       instance.name = name;  
       instance.filePrefix = filePrefix;  
       instance.layerType = layerType;  
       instance.addScaleRangeThemesToThemeDefination = true;  
       instance.addSelectionThemeToThemeDefination = true;  
       instance.populateThemeDefination = true;  
       instance.enableThematicScheme = false;  
       instance.load();  
       return instance;  
     }  
     public DefaultLayerBuilder Enable(bool val)  
     {  
       this.enabled = val;  
       return this;  
     }  
     public DefaultLayerBuilder Description(string val)  
     {  
       this.desc = val;  
       return this;  
     }  
     public ConfigurationLayer Build()  
     {  
       if (this.layerType == null)  
       {  
         throw new ArgumentNullException("layerType");  
       }   
       if(!this.LayerFunctionMap.ContainsKey(this.layerType))   
       {  
         throw new ArgumentException("layer type not found", "layerType");  
       }  
       ConfigurationLayer cl = new ConfigurationLayer();  
       string schemeName = this.layerType.Name.Replace("ConfigurationLayer", "")   
                 + (this.enableThematicScheme ? "_DEFAULT_THEMATICSCHEME" : "_DEFAULT_SCHEME");  
       dynamic typedLayer = this.LayerFunctionMap[this.layerType](schemeName);  
       PropertyInfo[] conLayerproInfoList = cl.GetType().GetProperties();  
       PropertyInfo conLayerproInfo = conLayerproInfoList.SingleOrDefault(i => i.PropertyType == this.layerType);  
       conLayerproInfo.SetValue(cl, typedLayer);  
       return cl;  
     }  
     private void load()  
     {  
       this.defaultScaleRangesData = getDefaultScaleRangesData();  
       this.defaultPointThemeData = getDefaultPointThemeData();  
       this.defaultPolylineThemeData = getDefaultPolylineThemeData();  
       this.defaultPolygonThemeData = getDefaultPolygonThemeData();  
       this.defaultPolylineLayerRoutingData = getDefaultPolylineLayerRoutingData();  
       this.defaultPolylineDirectionInfoData = getDefaultPolylineDirectionInfoData();  

       this.LayerFunctionMap = new Dictionary<Type, Func<string, dynamic>>();
       LayerFunctionMap.Add(typeof(ConfigurationLayerPolygonLayer),  
       schemeName => this.getTypedLayer<ConfigurationLayerPolygonLayer, ConfigurationLayerPolygonLayerScheme, ConfigurationLayerPolygonLayerSchemeScaleRanges,  
                   ConfigurationLayerPolygonLayerThematicScheme, ConfigurationLayerPolygonLayerThematicSchemeThematicScaleRanges,  
                   ConfigurationLayerPolygonLayerPolygonTheme>(schemeName, this.defaultPolygonThemeData));  
       LayerFunctionMap.Add(typeof(ConfigurationLayerPolylineLayer),  
       schemeName => this.getTypedLayer<ConfigurationLayerPolylineLayer, ConfigurationLayerPolylineLayerScheme, ConfigurationLayerPolylineLayerSchemeScaleRanges,  
                   ConfigurationLayerPolylineLayerPolylineTheme, ConfigurationLayerPolylineLayerDirectionInfo, ConfigurationLayerPolylineLayerRouting>  
                   (schemeName, this.defaultPolylineThemeData, this.defaultPolylineDirectionInfoData, this.defaultPolylineLayerRoutingData));  
       LayerFunctionMap.Add(typeof(ConfigurationLayerPointLayer),  
       schemeName => this.getTypedLayer<ConfigurationLayerPointLayer, ConfigurationLayerPointLayerScheme, ConfigurationLayerPointLayerSchemeScaleRanges,   
                   ConfigurationLayerPointLayerThematicScheme, ConfigurationLayerPointLayerThematicSchemeThematicScaleRanges,   
                   ConfigurationLayerPointLayerPointTheme>(schemeName, this.defaultPointThemeData));  
     }  
     private dynamic getTypedLayer<L, S, R, T, D, RT>(string schemeName, string defaultThemeData,  
                 string defaultDirectionInfoData, string defaultRoutingData)  
       where L : new()  
       where S : new()  
       where R : new()  
       where T : new()  
       where D : new()  
       where RT : new()  
     {  
       dynamic Layer = new L();  
       this.loadBasicInfoInTypedLayer(Layer);  
       Layer.Scheme = new List<S>();  
       Layer.Scheme.Add(new S());  
       Layer.Scheme[0].Name = schemeName;  
       Layer.Scheme[0].ScaleRanges =  
         defaultScaleRangesData  
         .Replace("<ScaleRanges>", "<" + typeof(R).Name + ">")  
         .Replace("</ScaleRanges>", "</" + typeof(R).Name + ">")  
         .XMLDeSerialize<R>();  
       Layer.Scheme[0].SchemeSelectionTheme = Layer.Scheme[0].Name + "_SelectionTheme";  
       if (this.populateThemeDefination)  
       {  
         Layer.ThemeDefinition = new List<T>();  
         if (this.addScaleRangeThemesToThemeDefination)  
         {  
           dynamic defaultRangeTheme =  
             defaultThemeData  
             .Replace("<Theme", "<" + typeof(T).Name + "")  
             .Replace("</Theme>", "</" + typeof(T).Name + ">")  
             .Replace("<DisplayProperties", "<" + typeof(L).Name.Replace("Configuration", "").Replace("Layer", "") + "Properties")  
             .XMLDeSerialize<T>();  
           defaultRangeTheme.Name = Layer.Scheme[0].ScaleRanges.DefaultRange.DefaultType.Theme;  
           Layer.ThemeDefinition.Add(defaultRangeTheme);  
           foreach (dynamic scaleRange in Layer.Scheme[0].ScaleRanges.ScaleRange)  
           {  
             dynamic scaleRangeTheme =  
             defaultThemeData  
             .Replace("<Theme", "<" + typeof(T).Name + "")  
             .Replace("</Theme>", "</" + typeof(T).Name + ">")  
             .Replace("<DisplayProperties", "<" + typeof(L).Name.Replace("Configuration", "").Replace("Layer", "") + "Properties")  
             .XMLDeSerialize<T>();  
             scaleRangeTheme.Name = scaleRange.DefaultType.Theme;  
             Layer.ThemeDefinition.Add(scaleRangeTheme);  
           }  
         }  
         if (this.addSelectionThemeToThemeDefination)  
         {  
           dynamic selectionTheme =  
           defaultThemeData  
           .Replace("<Theme", "<" + typeof(T).Name + "")  
           .Replace("</Theme>", "</" + typeof(T).Name + ">")  
           .Replace("<DisplayProperties", "<" + typeof(L).Name.Replace("Configuration", "").Replace("Layer", "") + "Properties")  
           .XMLDeSerialize<T>();  
           selectionTheme.Name = Layer.Scheme[0].SchemeSelectionTheme;  
           Layer.ThemeDefinition.Add(selectionTheme);  
         }  
         Layer.DirectionInfo = defaultDirectionInfoData.XMLDeSerialize<D>();  
         Layer.Routing = defaultRoutingData.XMLDeSerialize<RT>();  
       }  
       return Layer;  
     }  
     private dynamic getTypedLayer<L, S, R, TS, TR, T>(string schemeName, string defaultThemeData)  
       where L : new()  
       where S : new()  
       where R : new()  
       where TS : new()  
       where TR : new()  
       where T : new()  
     {  
       dynamic Layer = new L();  
       this.loadBasicInfoInTypedLayer(Layer);  
       if (this.enableThematicScheme)  
       {  
         Layer.ThematicScheme = new List<TS>();  
         Layer.ThematicScheme.Add(new TS());  
         Layer.ThematicScheme[0].Name = schemeName;  
         Layer.ThematicScheme[0].ThematicScaleRanges =  
           defaultScaleRangesData  
           .Replace("<ThematicScaleRanges>", "<" + typeof(TR).Name + ">")  
           .Replace("</ThematicScaleRanges>", "</" + typeof(TR).Name + ">")  
           .XMLDeSerialize<TR>();  
         Layer.ThematicScheme[0].SchemeSelectionTheme = Layer.ThematicScheme[0].Name + "_SelectionTheme";  
       }  
       else  
       {  
         Layer.Scheme = new List<S>();  
         Layer.Scheme.Add(new S());  
         Layer.Scheme[0].Name = schemeName;  
         Layer.Scheme[0].ScaleRanges =  
           defaultScaleRangesData  
           .Replace("<ScaleRanges>", "<" + typeof(R).Name + ">")  
           .Replace("</ScaleRanges>", "</" + typeof(R).Name + ">")  
           .XMLDeSerialize<R>();  
         Layer.Scheme[0].SchemeSelectionTheme = Layer.Scheme[0].Name + "_SelectionTheme";  
       }  
       if (this.populateThemeDefination)  
       {  
         Layer.ThemeDefinition = new List<T>();  
         if (this.addScaleRangeThemesToThemeDefination)  
         {  
           dynamic defaultRangeTheme =  
             defaultThemeData  
             .Replace("<Theme", "<" + typeof(T).Name + "")  
             .Replace("</Theme>", "</" + typeof(T).Name + ">")  
             .Replace("<DisplayProperties", "<" + typeof(L).Name.Replace("Configuration", "").Replace("Layer", "") + "Properties")  
             .XMLDeSerialize<T>();  
           defaultRangeTheme.Name = Layer.Scheme[0].ScaleRanges.DefaultRange.DefaultType.Theme;  
           Layer.ThemeDefinition.Add(defaultRangeTheme);  
           foreach (dynamic scaleRange in Layer.Scheme[0].ScaleRanges.ScaleRange)  
           {  
             dynamic scaleRangeTheme =  
             defaultThemeData  
             .Replace("<Theme", "<" + typeof(T).Name + "")  
             .Replace("</Theme>", "</" + typeof(T).Name + ">")  
             .Replace("<DisplayProperties", "<" + typeof(L).Name.Replace("Configuration", "").Replace("Layer", "") + "Properties")  
             .XMLDeSerialize<T>();  
             scaleRangeTheme.Name = scaleRange.DefaultType.Theme;  
             Layer.ThemeDefinition.Add(scaleRangeTheme);  
           }  
         }  
         if (this.addSelectionThemeToThemeDefination)  
         {  
           dynamic selectionTheme =  
             defaultThemeData  
             .Replace("<Theme", "<" + typeof(T).Name + "")  
             .Replace("</Theme>", "</" + typeof(T).Name + ">")  
             .Replace("<DisplayProperties", "<" + layerType.Name.Replace("Configuration", "").Replace("Layer", "") + "Properties")  
             .XMLDeSerialize<T>();  
           if (this.enableThematicScheme)  
           {  
             selectionTheme.Name = Layer.ThematicScheme[0].SchemeSelectionTheme;  
           }  
           else  
           {  
             selectionTheme.Name = Layer.Scheme[0].SchemeSelectionTheme;  
           }  
           Layer.ThemeDefinition.Add(selectionTheme);  
         }  
       }  
       return Layer;  
     }  
   }  
 }  

Sunday, June 14, 2015

XML in Java: Marshal (Part-1)

XML is a tag based format to describe data. Describing data is done by two ways using Element and Attribute. XML documents must contain one root element which is parent of all other elements. Moreover, XML can be validated against a set of rules. The document that holds those rules are called DTD. A DTD is the schema or grammar of the XML document which means that the XML document's elements and attributes are declared in that DTD and follow the grammatical rules that the DTD specifies for them. Also, There is another way of setting rules for XML document which is called XSD or XML schema. Today it gains more popularity over DTD, as it itself is in XML format unlike the DTD. Furthermore, There is XSL which is more like CSS for XML and used for displaying XML data. It transform an XML document into HTML. There are also other related technologies like XPath and XML Parsers. For more information please checkout w3schools.  

XML is heavily used for data storage and transfer. Websites have large usage of XML. SOA based web services and REST services, which closely depend on XML are also increasing rapidly in number. Therefore, it's essential to developers regardless of which  platform they work in, to have a depth knowledge in XML such as xmlns for defining namespace to avoid conflict among XML Elements, XSD, DTD for xml validation, XSL for presenting, parsers like browser supported DOM parser for manipulating XML data in front end and special parser for handling large XML files.

Developers also need to know about Marshaling/Serializing to convert Object to XML, Unmarshaling/Deserializing to convert XML to Object, and usage of XSD to auto-generate Class that represents the xml. The last part is especially important while the XML itself is large.  In Java, JAXP library gives the support of different parsers and JAXB gives the XML binding facility with Marshaling and Unmarshaling support. In .Net, XML Serializing and Deserializing support are given by System.Xml.Serialization Namespace about which I'll discuss in later post. Today, our focus will be on XML binding support of Java and usage of JAXB library.

Here, I've used JDK 1.7 and netbeans 7.4 for my application. As JDK1.6 or above already include the JAXB library, we do not need to add the library in our application explicitly. You can download the source code from here.


Now, let me show how Marshaling is done by creating a simple class as example.

Student.java

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class Student {
   
    private int id;
    private String firstName;
    private String lastName;
    private String email;

    public int getId() { return id; }
    public void setId(int id) { this.id = id; }
    public String getFirstName() { return firstName; }
    public void setFirstName(String firstName) { this.firstName = firstName; }
    public String getLastName() { return lastName; }
    public void setLastName(String lastName) { this.lastName = lastName; }
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }
}

While creating a class for marshaling we have to set @XmlRootElement annotation as every XML has a root element. Here I've used @XmlRootElement annotation to define "Student" as root element of the generated XML.

Code to marshal the above class: 

JAXBTutorial.java

    public static void main(String[] args) {
        Student student = new Student();
        student.setId(1);
        student.setFirstName("Pervez");
        student.setLastName("Sajjad");
        student.setEmail("pervez.cse@gmail.com");
        String result = marshal(student);
        System.out.println(result);
    }
   
    public static String marshal(Student student) {
        String result = null;
        try {
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            JAXBContext jaxbContext = JAXBContext.newInstance(Student.class);
            Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
            jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); // pretty output
            jaxbMarshaller.marshal(student, out);
            result = out.toString();
        } catch (JAXBException e) {
            e.printStackTrace();
        }
        return result;
    }

You will find JAXBContext, Marshaller classes under javax.xml.bind package.
The output after the marshaling is :

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<student>
    <email>pervez.cse@gmail.com</email>
    <firstName>Pervez</firstName>
    <id>1</id>
    <lastName>Sajjad</lastName>
</student>

XML Marshaling and Annotations

Some common Annotations that are available while Marshaling are:
  • @XmlAttribute: This member will be marshaled as an XML attribute.
  • @XmlElement: The field will be marshaled as an XML element.
  • @XmlTransient: Field will be ignored while marshaling.
  • @XmlType to indicate special options like appearance order of the sibling elements in the XML. 

@XmlElement:
If we require different xml tag name from the instance variable name we can introduce the @XmlElement annotation to it in the class structure.

@XmlRootElement
public class Student {
   
    private int id;
    private String firstName;
    private String lastName;
    private String email;

    public int getId() { return id; }
   
    @XmlElement(name = "given name")
    public String getFirstName() { return firstName; }
   
    @XmlElement(name = "surname")
    public String getLastName() { return lastName; }
    public String getEmail() { return email; }
   
    public void setId(int id) { this.id = id; }
    public void setFirstName(String firstName) { this.firstName = firstName; }
    public void setLastName(String lastName) { this.lastName = lastName; }
    public void setEmail(String email) { this.email = email; }
}

@XmlElement(name = "given name") specifies that the instance variable firstName will be marshaled with the tag name "given name" in the XML. It help us to map between the XML tag name and the instance variable name. Like this you can also change the name of XML attribute and XML root element.

The produced XML output with the custom tag name is:

<student>
    <email>pervez.cse@gmail.com</email>
    <given name>Pervez</given name>
    <id>1</id>
    <surname>Sajjad</surname>
</student>

@XmlAttribute:
If we want that the instance variable id should occur as the attribute for the tag student then we should use @XmlAttribute. @XmlAttribute serializes the instance variable as the attribute for the parent tag.
The following code illustrates the functionality:

@XmlRootElement
public class Student {
   
    private int id;
    private String firstName;
    private String lastName;
    private String email;

    @XmlAttribute(name = "identification")
    public int getId() { return id; }
    public String getFirstName() { return firstName; }
    public String getLastName() { return lastName; }
    public String getEmail() { return email; }
   
    public void setId(int id) { this.id = id; }
    public void setFirstName(String firstName) { this.firstName = firstName; }
    public void setLastName(String lastName) { this.lastName = lastName; }
    public void setEmail(String email) { this.email = email; }
}

The produced XML output for the code will be:

<student identification="1">
    <email>pervez.cse@gmail.com</email>
    <firstName>Pervez</firstName>
    <lastName>Sajjad</lastName>
</student>

Since the getId() method is annotated by @XmlAttribute(name = "identification") therefore the instance variable id is included as an attribute named "identification" for the parent tag student.

@XmlTransient:
By default, all instance variables with public read/write access are marshalled by the Marshaller . That is, the value of each publicly accessible variable is persisted as an XML element or XML attribute in an XML-document instance. In order to override this property apply @XmlTransient annotation to it. This will remove the element from the XML.
The code below explains the following:

@XmlRootElement
public class Student {
   
    private int id;
    private String firstName;
    private String lastName;
    private String email;

    public int getId() { return id; }
   
    @XmlTransient
    public String getFirstName() { return firstName; }
    public String getLastName() { return lastName; }
    public String getEmail() { return email; }
   
    public void setId(int id) { this.id = id; }
    public void setFirstName(String firstName) { this.firstName = firstName; }
    public void setLastName(String lastName) { this.lastName = lastName; }
    public void setEmail(String email) { this.email = email; }
}

Here we can see that the getFirstName() method is annotated by @XmlTransient. The produced XML output will not contain the firstName tag in it.

<student>
    <email>pervez.cse@gmail.com</email>
    <id>1</id>
    <lastName>Sajjad</lastName>
</student>


@XmlType:
By using @XmlType annotation we can define the appearance order of the sibling elements in the XML.
The code below explains the following:

@XmlRootElement
@XmlType(propOrder = {"id", "firstName", "lastName", "email"})
public class Student {
   
    private int id;
    private String firstName;
    private String lastName;
    private String email;

    public int getId() { return id; }
    public String getFirstName() { return firstName; }
    public String getLastName() { return lastName; }
    public String getEmail() { return email; }
   
    public void setId(int id) { this.id = id; }
    public void setFirstName(String firstName) { this.firstName = firstName; }
    public void setLastName(String lastName) { this.lastName = lastName; }
    public void setEmail(String email) { this.email = email; }
}

The produced XML output of the code is as follows:

<student>
    <id>1</id>
    <firstName>Pervez</firstName>
    <lastName>Sajjad</lastName>
    <email>pervez.cse@gmail.com</email>
</student>

Here we see the elements are appeared as it is defined by the @XmlType annotation.


Marshaling of a class with instance variable of other class object

If we have a class structure such that a class contains an object of other class and we want to include that class object also for serialization.
Let's see the following example :

@XmlRootElement
public class Student {
   
    private int id;
    private String firstName;
    private String lastName;
    private String email;
    private Department department;

    public int getId() { return id; }
    public String getFirstName() { return firstName; }
    public String getLastName() { return lastName; }
    public String getEmail() { return email; }
    public Department getDepartment() { return department; }   
    public void setId(int id) { this.id = id; }
    public void setFirstName(String firstName) { this.firstName = firstName; }
    public void setLastName(String lastName) { this.lastName = lastName; }
    public void setEmail(String email) { this.email = email; }
    public void setDepartment(Department department) { this.department = department; }
}

public class Department {
   
    private int id;
    private String name;
    private String school;

    public int getId() { return id; }
    public String getName() { return name; }
    public String getSchool() { return school; }
    public void setName(String name) { this.name = name; }
    public void setId(int id) { this.id = id; }
    public void setSchool(String school) { this.school = school; }
}

This is how the Student class will be serialized:

<student>
    <department>
        <id>1</id>
        <name>cse</name>
        <school>Applied Science</school>
    </department>
    <email>pervez.cse@gmail.com</email>
    <firstName>Pervez</firstName>
    <id>1</id>
    <lastName>Sajjad</lastName>
</student>

Now, consider the following XML. It is a bit complex compared to the the previous one. Please check the department elements of both XMLs.

<student>
    <department id="1" school="Applied Science">cse</department>
    <email>pervez.cse@gmail.com</email>
    <firstName>Pervez</firstName>
    <id>1</id>
    <lastName>Sajjad</lastName>
</student>


How can we generate this type of XML? The following code is the answer which can generate this XML.

public class Department {
   
    private int id;
    private String name;
    private String school;

    @XmlAttribute
    public int getId() { return id; }
    @XmlValue
    public String getName() { return name; }
    @XmlAttribute
    public String getSchool() { return school; }
   

    public void setName(String name) { this.name = name; }
    public void setId(int id) { this.id = id; }
    public void setSchool(String school) { this.school = school; }
}


There is no change in Student class. However, in Department class we've used @XmlValue to add the department name as the InnerText to the tag "department". One element can have only one InnerText.

Marshaling of a class containing collection of objects

To marshal List like collection we need two annotations which are: @XmlElementWrapper and @XmlElement. @XmlElementWrapper is used to define the tag-name of the list in XML. On the other hand, @XmlElement is used to define the tag-name of the individual members in that list.
Let's check the code:

@XmlRootElement
public class Student {
  
    private int id;
    private String firstName;
    private String lastName;
    private String email;
    private List<Course> courseList;

    public Student() {
        this.courseList = new ArrayList<>();
    }

    public int getId() { return id; }
    public String getFirstName() { return firstName; }
    public String getLastName() { return lastName; }
    public String getEmail() { return email; }


    @XmlElementWrapper(name = "course-list")
    @XmlElement(name = "course")
    public List<Course> getCourseList() { return courseList; }
  
    public void setId(int id) { this.id = id; }
    public void setFirstName(String firstName) { this.firstName = firstName; }
    public void setLastName(String lastName) { this.lastName = lastName; }
    public void setEmail(String email) { this.email = email; }   

    public void setCourseList(List<Course> courseList) { this.courseList = courseList; }
}


public class Course {
    private int id;
    private String name;
    private double credit;

    public int getId() { return id; }
    public String getName() { return name; }
    public double getCredit() { return credit; }
    public void setName(String name) { this.name = name; }   
    public void setId(int id) { this.id = id; }
    public void setCredit(double credit) { this.credit = credit; }
}


 The generated XML output is as follows:

 <student>
    <course-list>
        <course>
            <credit>3.0</credit>
            <id>1</id>
            <name>CSE-101</name>
        </course>
        <course>
            <credit>4.0</credit>
            <id>2</id>
            <name>CSE-102</name>
        </course>
    </course-list>
    <email>pervez.cse@gmail.com</email>
    <firstName>Pervez</firstName>
    <id>1</id>
    <lastName>Sajjad</lastName>
</student>


XML Unmarshal

In the 2nd part we'll discuss how to unmarshal XML to Object. Please keep in touch

Conclusion

 JAXB provides us a very effective way to convert XML to object and vice versa. Hence, it is used in other api extensively for XML binding support. One of them is JAX-WS library which facilitates SOAP based web service support and is part of the JEE stack.

Useful Links

1. http://www.codeproject.com/Articles/483055/XML-Serialization-and-Deserialization-Part-1
2. http://www.javacodegeeks.com/2014/12/jaxb-tutorial-xml-binding.html