• 推荐
  • 评论
  • 收藏

如何将List<T>转换相应的Html(xsl动态转换)(一)

2022-12-02    1057次浏览

一、前言

根据指定的xsl样式将List<T>转换相应的Html,其中涉及到怎样将List<T>转换成DataTable,如何将xml文本、xsl样式文本动态转换成html以及如何设置以及控制xsl样式。主要步骤如下:

步骤一、将List<T>转换成DataTable

步骤二、将Xml与Xsl动态转换成Html

步骤三、设置以及控制Xsl的内容样式。

以上的三个步骤本人将以此顺序介绍相关的内容,分别对应相关的随笔,因为本人上班的时候不能上网以及时间上的问题,所以才将该文章分3次来写。

二、类图设计

以上的内容涉及的类图虽然很简单,但是本人还是花了不少时间的来实现具体功能,代码质量还是可以保证的。EntityMapper负责将List<T>转换成DataTable(目前先将就着用这个,还有一个比较复杂的,涉及的东西比较多,此处不会讲解,以后有时间再写),XslTransform负责将xml文本、xsl文本转换成html,XslTransformFacade外观模式封装其中的细节,如下:

 

三、将List<T>转换成DataTable,再转换成xml的具体实现

这里主要是个思路的问题,先将List<T>转换成DataTable,然后将DataTable添加到DataSet中,最后通过DataSet的GetXml()方法获取Xml内容。这里思路正确的话,实现不是很复杂。先看下XslTransformFacade类,基本思路都封装在该类中:

 1     public class XslTransformFacade
 2     {
 3         public static string ToHtml<T>(List<T> entities,string xslText) where T : new()
 4         {
 5             if (ValidateArgs(entities,xslText))
 6             {
 7                 return string.Empty;
 8             }
 9 
10             string xmlText= GetXmlText<T>(entities);
11 
12             using (XslTransform xslTransform = new XslTransform())
13             {
14                 return xslTransform.Transfer(xmlText, xslText);
15             }
16         }
17 
18         private static bool ValidateArgs<T>(List<T> entities, string xslText)
19         {
20             return entities == null || entities.Count == 0 
21                 || string.IsNullOrWhiteSpace(xslText);
22         }
23 
24         private static string GetXmlText<T>(List<T> entities) where T : new()
25         {
26             using (DataSet dataSet = new DataSet("DataSet"))
27             {
28                 DataTable dataTable = EntityMapper.ToDataTable<T>(entities);
29                 dataSet.Tables.Add(dataTable);
30                 return dataSet.GetXml();
31             }
32         }
33     }

这一步骤主要是通过GetXmlText<T>(List<T> entities)方法来实现的。EntityMapper.ToDataTable<T>(entities)方法将在下面介绍。生成的Xml如下(示例):

-<DataSet>
- <MapperInfo>
  
<Name>MapperInfoIndex0</Name> 
  
<CreatedTime>2011-05-24T00:27:23.734375+08:00</CreatedTime> 
  
<IsActive>true</IsActive> 
  
<Value>0</Value> 
  
<Percent>0</Percent> 
  
<TargetUrl>www.codeplex.com?Id=0</TargetUrl> 
  
</MapperInfo>
- <MapperInfo>
  
<Name>MapperInfoIndex1</Name> 
  
<CreatedTime>2011-05-24T00:27:23.734375+08:00</CreatedTime> 
  
<IsActive>false</IsActive> 
  
<Value>1</Value> 
  
<TargetUrl>www.codeplex.com?Id=1</TargetUrl> 
  
</MapperInfo>
-</DataSet>

其次第12-15行主要是将xmlText、xslText转换成html,这将在后续的文章中介绍具体实现:

12             using (XslTransform xslTransform = new XslTransform())
13             {
14                 return xslTransform.Transfer(xmlText, xslText);
15             }

四、将List<T>转换成DataTable

这里主要是通过反射将List<T>转换成DataTable。其中需要设置DataTable的DataColumnCollection集合以及设置DataRowCollection集合。具体代码如下:

 1     public sealed class EntityMapper 
 2     {
 3         public static DataTable ToDataTable<T>(List<T> entities) where T : new()
 4         {
 5             if (entities == null || entities.Count == 0)
 6             {
 7                 return new DataTable();
 8             }
 9 
10             using (DataTable dataTable = new DataTable(typeof(T).Name))
11             {
12                 PropertyInfo[] properties = typeof(T).GetProperties();
13 
14                 SetColumnsType(properties, dataTable);
15                 SetTableContent<T>(entities, properties, dataTable);
16 
17                 return dataTable;
18             }
19         }
20 
21         private static void SetTableContent<T>(List<T> entities, 
22             PropertyInfo[] properties, DataTable dataTable)
23         {
24             foreach (T entity in entities)
25             {
26                 AddTableRowsAndContent<T>(properties, entity,dataTable);
27             }
28         }
29 
30         private static void AddTableRowsAndContent<T>(PropertyInfo[] properties,
31             T entity, DataTable dataTable)
32         {
33             DataRow newRow = dataTable.NewRow();
34             foreach (PropertyInfo propertyInfo in properties)
35             {
36                 if (!CanGetPropertyValue(propertyInfo,dataTable))
37                 {
38                     continue;
39                 }
40 
41                 try
42                 {
43                     object objValue = propertyInfo.GetValue(entity, null);
44                     newRow[propertyInfo.Name] = objValue ?? DBNull.Value;
45                 }
46                 finally 
47                 {
48                 }
49             }
50 
51             dataTable.Rows.Add(newRow);
52         }
53 
54         private static bool CanGetPropertyValue(PropertyInfo propertyInfo, DataTable dataTable)
55         {
56             return propertyInfo.CanRead && 
57                 dataTable.Columns.Contains(propertyInfo.Name);
58         }
59 
60         private static void SetColumnsType(PropertyInfo[] properties, DataTable dataTable)
61         {
62             Type colType = null;
63 
64             foreach (PropertyInfo propInfo in properties)
65             {
66                 if (propInfo.PropertyType.IsGenericType)
67                 {
68                     colType = Nullable.GetUnderlyingType(propInfo.PropertyType);
69                 }
70                 else
71                 {
72                     colType = propInfo.PropertyType;
73                 }
74 
75                 if (colType.FullName.StartsWith("System"))
76                 {
77                     dataTable.Columns.Add(propInfo.Name, colType);
78                 }
79             }
80         }
81      }

这里主要的操作步骤为以下2行代码:

14                 SetColumnsType(properties, dataTable);
15                 SetTableContent<T>(entities, properties, dataTable);

14行设置列的类型,15行设置DataRowCollection的DataRow内容,对于上面的代码,再多的解释都是无用的,看具体代码就行了。

 

其中需要解释一下的就是54-58行:

54         private static bool CanGetPropertyValue(PropertyInfo propertyInfo, DataTable dataTable)
55         {
56             return propertyInfo.CanRead && 
57                 dataTable.Columns.Contains(propertyInfo.Name);
58         }
因为当属性不可读,并且DataColumnCollection不包行该属性名时,赋值可能会抛出异常的。

其次还提一下第10行:

10             using (DataTable dataTable = new DataTable(typeof(T).Name))

这里用using来释放资源,主要是使代码中不存在任何警告,警告有时候使系统奔溃也有可能。否则进行代码分析的时候会出现警告信息,如下图所示:

 

五、DataTable 转换成List<T> (这个是附加的,与此主题无关,但是也是EntityMapper 的一部分)

虽然以前写了一篇《 将DataRow转换成相应的对象(通用以及泛型操作) 》 ,但是并不是将DataTable 转换成List<T>,后续也有些地方进行了改进,代码如下:

public static List<T> ToEntities<T>(DataTable dataTable) where T : new()
        {
            List
<T> entities = new List<T>();
            
if (dataTable == null || dataTable.Rows == null
                
|| dataTable.Rows.Count == 0)
            {
                
return entities;
            }

            
foreach (DataRow dataRow in dataTable.Rows)
            {
                entities.Add(ToEntity
<T>(dataRow));
            }

            
return entities;
        }

        
public static T ToEntity<T>(DataRow dataRow) where T : new()
        {
            
if (dataRow == null)
            {
                
return default(T);
            }

            T item 
= Activator.CreateInstance<T>();
            CopyToEntity(item, dataRow);

            
return item;
        }

        
public static void CopyToEntity(object entity, DataRow dataRow)
        {
            
if (entity == null || dataRow == null)
            {
                
return;
            }
            PropertyInfo[] propertyInfos 
= entity.GetType().GetProperties();

            
foreach (PropertyInfo propertyInfo in propertyInfos)
            {
                
if (!CanSetPropertyValue(propertyInfo, dataRow))
                {
                    
continue;
                }

                
try
                {
                    
if (dataRow[propertyInfo.Name] is DBNull)
                    {
                        propertyInfo.SetValue(entity, 
nullnull);
                        
continue;
                    }
                    SetPropertyValue(entity, dataRow, propertyInfo);
                }
                
finally
                {
                }
            }
        }

        
private static bool CanSetPropertyValue(PropertyInfo propertyInfo,
            DataRow dataRow)
        {
            
return propertyInfo.CanWrite && 
                dataRow.Table.Columns.Contains(propertyInfo.Name); 
        }

        
private static void SetPropertyValue(object entity,
            DataRow dataRow, PropertyInfo propertyInfo)
        {
            
if (propertyInfo.PropertyType == typeof(DateTime?||
                propertyInfo.PropertyType 
== typeof(DateTime))
            {
                DateTime date 
= DateTime.MaxValue;
                DateTime.TryParse(dataRow[propertyInfo.Name].ToString(),
                    CultureInfo.CurrentCulture, DateTimeStyles.None, 
out date);

                propertyInfo.SetValue(entity, date, 
null);
            }
            
else
            {
                propertyInfo.SetValue(entity, dataRow[propertyInfo.Name], 
null);
            }
        }

六、EntityMapper.ToDataTable<T>(List<T> entities)方法的单元测试

ToDataTable<T>(List<T> entities)方法的具体单元测试如下,仅仅是主要功能的测试:

public class EntityMapperTest
 2     {
 3         [TestMethod()]
 4         public void ToDataTableTest()
 5         {
 6             List<MapperInfo> entities = CreateMapperInfos(9);
 7             DataTable result = EntityMapper.ToDataTable<MapperInfo>(entities);
 8             Assert.IsNotNull(result);
 9             Assert.IsNotNull(result.Rows);
10             Assert.AreEqual(9, result.Rows.Count);
11             int index = 0;
12             foreach (DataRow dataRow in result.Rows)
13             {
14                 Assert.AreEqual(dataRow["Name"], 
15                     string.Concat("MapperInfoIndex", index.ToString()));
16                 Assert.AreEqual(dataRow["IsActive"], index % 2 == 0 ? true : false);
17                 Assert.AreEqual(dataRow["Value"], index);
18                 Assert.IsNotNull(dataRow["CreatedTime"]);
19                 Assert.AreEqual(dataRow["Percent"], DBNull.Value);
20                 index++;
21                 
22             }
23         }
24 
25         private List<MapperInfo> CreateMapperInfos(int count)
26         {
27             List<MapperInfo> entities = new List<MapperInfo>();
28             for (int index = 0; index < count; index++)
29             {
30                 entities.Add(new MapperInfo()
31                 {
32                     Name = string.Concat("MapperInfoIndex", index.ToString()),
33                     IsActive = (index % 2 == 0 ? true : false),
34                     CreatedTime = DateTime.Now,
35                     Value = index
36                 });
37             }
38 
39             return entities;
40         }
41     }

七、总结

最近二个多月,忙死我了。这段时间转SIT测试了,终于又可以轻松一阵了,哈哈。这些随笔的内容都是上班时间之外写的,每次写的代码都做单元测试,主要是避免出现显而易见的BUG,以及将代码积累并且更新到自己的框架中。接下来的一篇主要是如何将xml文本、xsl文本动态转换成html,这个花费了本人一段时间,一直在摸索才整理出来的,主要是通过XslCompiledTransform.Transform(XmlReader input, XmlWriter results)来实现的,本人已经更新了几个版本了,这个应该是最终版本的,同时也是最精简的版本。

原文地址:https://www.cnblogs.com/Leo_wl/p/2059313.html