如何将List<T>转换相应的Html(xsl动态转换)(一)
一、前言
根据指定的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类,基本思路都封装在该类中:
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如下(示例):
- <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集合。具体代码如下:
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>,后续也有些地方进行了改进,代码如下:
{
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, null, null);
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)方法的具体单元测试如下,仅仅是主要功能的测试:
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)来实现的,本人已经更新了几个版本了,这个应该是最终版本的,同时也是最精简的版本。