利用XML实现通用WEB报表输入和输出

开发B/S结构的应用程序最头疼的问题可能就是报表打印了,而很多B/S结构的应用程序常常需要完成非常复杂的报表打印任务。更加郁闷的是,很多报表在使用的的时候还要求将数据输入到数据库中。
  方案原理
  其实原理很简单,通过XML强大的自定义功能,进行格式解析,我们便能方便的自定义出我们所有需要的格式控制标签,在服务器端进行动态编码后通过WEB服务器传到客户端,然后在客户端打印出我们需要的报表。
        感谢卢彦的利用XML实现通用WEB报表打印(http://www.yesky.com/20030214/1652186.shtml),所以遵循他的精神,不敢独享
  
工作流程
  服务器的工作流程为:
  1. 根据报表类型读取xml模板。
2. 根据报表id读取数据库(如果采用orm的话,也可以是持久对象)的数据。
3. 对xml模板进行数据解析。
4. 构造html标签,并赋值 。
5.用户在客户端进行数据输入或者选择打印
6用户提交后,根据xml模板,对返回值进行解析,将数据保存。
 安全性
  由于采用的是普通WEB服务器传送数据,因此可以直接采用SSL安全套接字等已经成熟的WEB加密技术,保证了传输的安全性。
  由于采用的是80端口,不需要再另外新增加专用端口,减少了安全漏洞的可能性,同时还能方便的穿过双方的的网络防火墙等保护设备。
      格式定义
为了能自己控制打印的格式,我们定义了下列的格式标签
  标签应用示例:
<? xmlversion="1.0"encoding="utf-8" ?>
< doc >
     
< pagesetting >
         
< paperwidth > 800 </ paperwidth >
         
< paperheight > 600 </ paperheight >
         
< pageleft > 0 </ pageleft >
         
< pageright > 0 </ pageright >
         
< pagetop > 0 </ pagetop >
         
< pagebottom > 0 </ pagebottom >
     
</ pagesetting >
     
< data >
         
< DataClass > XReport.ReportDocument, XReport </ DataClass >
         
< get > GetData </ get >
         
< set > SetData </ set >
     
</ data >
     
< report >
         
< reporthead >
              
< rowheight ="20" ></ row >
              
< rowheight ="40" >
                   
< textalign ="center" fontsize ="12pt" > 通用报表 </ text >
              
</ row >
              
< rowheight ="20" >
                   
< textalign ="center" fontsize ="10pt" > ([$Eval(get(year),set(year)),Format(align(center),size(50))$]年[$Eval(get(month),set(month))$]月) </ text >
              
</ row >
              
< rowheight ="16" >
                   
< textalign ="set" x ="0" y ="0" fontsize ="9pt" > 填报单位: [$Eval(get(dw),set(dw)),Format(align(center),size(50))$] </ text >
                   
< textalign ="set" x ="325" y ="0" fontsize ="9pt" > 文  号: 报字[1999]7号 </ text >
              
</ row >
              
< rowheight ="18" ></ row >
         
</ reporthead >
         
< reporttablepagesize ="20" >
              
< columnscolumnsize ="11" >
                   
< columnkey ="c1" align ="center" > [$Eval(get(DataItem.#c1)),Format(align(center),size(100%))$] </ column >
                   
< columnkey ="c2" align ="right" > [$Eval(get(DataItem.#c2),set(DataItem.#c2)),Format(align(right),size(100%))$] </ column >
                   
< columnkey ="c3" align ="right" > [$Eval(get(DataItem.#c3),set(DataItem.#c3)),Format(align(right),size(100%))$] </ column >
                   
< columnkey ="c4" align ="right" > [$Eval(get(DataItem.#c4),set(DataItem.#c4)),Format(align(right),size(100%))$] </ column >
                   
< columnkey ="c5" align ="right" > [$Eval(get(DataItem.#c5),set(DataItem.#c5)),Format(align(right),size(100%))$] </ column >
                   
< columnkey ="c6" align ="right" > [$Eval(get(DataItem.#c6),set(DataItem.#c6)),Format(align(right),size(100%))$] </ column >
                   
< columnkey ="c7" align ="right" > [$Eval(get(DataItem.#c7),set(DataItem.#c7)),Format(align(right),size(100%))$] </ column >
                   
< columnkey ="c8" align ="right" > [$Eval(get(DataItem.#c8),set(DataItem.#c8)),Format(align(right),size(100%))$] </ column >
                   
< columnkey ="c9" align ="right" > [$Eval(get(DataItem.#c9),set(DataItem.#c9)),Format(align(right),size(100%))$] </ column >
                   
< columnkey ="c10" align ="right" > [$Eval(get(DataItem.#c10),set(DataItem.#c10)),Format(align(right),size(100%))$] </ column >
                   
< columnkey ="c11" align ="right" > [$Eval(get(DataItem.#c11),set(DataItem.#c11)),Format(align(right),size(100%))$] </ column >
              
</ columns >
              
< rows >
                   
< rowkey ="r1" fontsize ="9pt" align ="center" >
                       
< cellrowspan ="2" colspan ="1" > 项目 </ cell >
                       
< cellrowspan ="1" colspan ="2" > 项目 </ cell >
                       
< cellrowspan ="2" colspan ="1" > 项目 </ cell >
                       
< cellrowspan ="2" colspan ="1" > 项目/n(个) </ cell >
                       
< cellrowspan ="2" colspan ="1" > 项目 </ cell >
                       
< cellrowspan ="1" colspan ="4" > 项目 </ cell >
                       
< cellrowspan ="2" colspan ="1" > 项目/n(元) </ cell >
                   
</ row >
                   
< rowkey ="r2" fontsize ="9pt" align ="center" >
                       
< cellrowspan ="1" colspan ="1" > 县/n(市)(个) </ cell >
                       
< cellrowspan ="1" colspan ="1" > 乡/n(镇)(个) </ cell >
                       
< cellrowspan ="1" colspan ="1" > 总计/n(人) </ cell >
                       
< cellrowspan ="1" colspan ="1" > 项目/n(人) </ cell >
                       
< cellrowspan ="1" colspan ="1" > 项目/n(人) </ cell >
                       
< cellrowspan ="1" colspan ="1" > 项目/n(人) </ cell >
                   
</ row >
                   
< rowkey ="r3" fontsize ="9pt" align ="center" >
                       
< cellrowspan ="1" colspan ="1" > 1 </ cell >
                       
< cellrowspan ="1" colspan ="1" > 2 </ cell >
                       
< cellrowspan ="1" colspan ="1" > 3 </ cell >
                       
< cellrowspan ="1" colspan ="1" > 4 </ cell >
                       
< cellrowspan ="1" colspan ="1" > 5 </ cell >
                       
< cellrowspan ="1" colspan ="1" > 6 </ cell >
                       
< cellrowspan ="1" colspan ="1" > 7 </ cell >
                       
< cellrowspan ="1" colspan ="1" > 8 </ cell >
                       
< cellrowspan ="1" colspan ="1" > 9 </ cell >
                       
< cellrowspan ="1" colspan ="1" > 10 </ cell >
                       
< cellrowspan ="1" colspan ="1" > 11 </ cell >
                   
</ row >
              
</ rows >
         
</ reporttable >
         
< reportfoot >
              
< rowheight ="20" >
                   
< textalign ="set" x ="0" y ="0" fontsize ="9pt" > 单位负责人:[$Eval(get(fzr),set(fzr)),Format(align(center),size(50))$] </ text >
                   
< textalign ="set" x ="80" y ="0" fontsize ="9pt" > 统计负责人:[$Eval(get(tjr),set(tjr)),Format(align(center),size(50))$] </ text >
                   
< textalign ="set" x ="140" y ="0" fontsize ="9pt" > 填报人:[$Eval(get(tbr),set(tbr)),Format(align(center),size(50))$] </ text >
                   
< textalign ="set" x ="200" y ="0" fontsize ="9pt" > 报出日期:[$Eval(get(tbyear),set(tbyear)),Format(align(center),size(50))$]年 </ text >
              
</ row >
         
</ reportfoot >
         
< calcus >
              
< calcu > [% #r20c2$ = parseValue(#r4c2$,1) * parseValue(#r5c2$,1) + parseValue(#r6c2$,0) + parseValue(#r7c2$,0) %] </ calcu >
              
< calcu > [% #r20c3$ = parseValue(#r4c3$,0) + parseValue(#r5c3$,0) + parseValue(#r6c3$,0) + parseValue(#r7c3$,0) %] </ calcu >
              
< calcu > [% #r20c4$ = parseValue(#r4c4$,0) + parseValue(#r5c4$,0) + parseValue(#r6c4$,0) + parseValue(#r7c4$,0) %] </ calcu >
         
</ calcus >
     
</ report >
</ doc >

   注意事项:
  a) 字符编码应该为UTF-8,否则会出现编码错误问题。
  b) 应该严格按照XML规定的格式来生成文件,否则XML解析器将不会予以解析。
  2. 客户端
      效果示例图
      打印预览
.Net Web控件方案的实现和扩充
  软件原理: 
该软件的原理其实很简单,就是要方便的解析出定义好的XML格式标记,解读出文件中标记的参数定义,构造出html标签。
  针对大多数报表的功能需要,我只定义了两种格式标签:文本(text)和表格(table),它们的具体属性定义和另外一些设置性的标签定义请参考前文,这里再补充一幅结构图帮助读者理解。如下所示:

此主题相关图片如下:

< doc >根节点
< pagesetting >
页面设置
< data >
报表数据
< report >
报表格式
< row >
< text >
文本
< reportfoot >
页脚
< reporthead >
表头
< calcus >
报表计算
计算
< columns >
< rows >
  <reporttable>
表格

结构设计:
  为了描述所有的样式标记,我先定义了一个抽象基类 ReportElement ,它拥有一个虚拟方法Draw,然后对应表格和文本,从 ReportElement 派生出三个子类,分别是 Report Table和 Report Text, ReportRow ,我还创建了一个 ReportParser 类用来解析不同的样式标记.

  代码实现:
 
  然后为项目添加一个PrintElement的新类,代码如下:
namespace  XReport
{
     
///<summary>
     
/// 报表成员
     
///</summary>

     public class ReportElement
     
{
         
public ReportElement()
         
{
              
         }

 
         
///<summary>
         
/// 绘制报表成员
         
///</summary>
         
///<param name="parent"></param>
         
///<returns></returns>

         public virtual bool Draw(System.Web.UI.HtmlControls.HtmlControl parent)
         
{
              
return false;
         }

}


  该类中只有一个虚拟方法Draw,注意它规定需要返回一个bool值,这个值的作用是用来指示标签是否在页内打印完毕。
  然后再添一个 ReportText 的新类,代码如下:
using  System.Xml;
using  System.Drawing;
using  System.Web;
using  System.Web.UI;
using  System.Web.UI.HtmlControls;
using  System.Collections;
 
namespace  XReport
{
     
///<summary>
     
/// 文本
     
///</summary>

     public class ReportText : ReportElement
     
{
         
private XmlNode text;
         
         
///<summary>
         
/// 构造器
         
///</summary>
         
///<param name="x"></param>
         
///<param name="d"></param>

         public ReportText(XmlNode x)
         
{
              text 
= x;
         }

 
         
///<summary>
         
/// 绘制报表成员
         
///</summary>
         
///<param name="parent"></param>
         
///<returns></returns>

         public override bool Draw(System.Web.UI.HtmlControls.HtmlControl parent)
         
{
              HtmlContainerControl c 
= new HtmlGenericControl("span");//添加显示控件
              c.Style.Add("Font-size",text.Attributes["fontsize"].InnerText);//显示字体
 
              
//设置显示位置
              if(text.Attributes["align"].InnerText != "set"
              
{
                   
//设置对齐方式
                   c.Style.Add("width","100%");
                   c.Style.Add(
"TEXT-ALIGN",text.Attributes["align"].InnerText);
              }

              
else
              
{
                   
//流对齐,及设置在流中的位置
                   c.Style.Add("POSITION","relative");
                   c.Style.Add(
"left",text.Attributes["x"].InnerText);
              }

 
              
string shtml = Encode(text.InnerText);//显示内容
 
              c.InnerHtml 
= shtml;//转换参数
              parent.Controls.Add(c);//将控件放入控父件中
 
              
return true;
         }

     }

}

添加一个解析器,使用正则表达式,把参数字符串提取出来
 
using  System;
using  System.Xml;
using  System.Collections;
using  System.Text.RegularExpressions;
namespace  XReport
{
     
///<summary>
     
/// 报表成员解析器
     
///</summary>

     public class ReportParser
     
{
 
         
///<summary>
         
/// 构造器
         
///</summary>

         public ReportParser()
         
{
         }

         
         
///<summary>
         
/// 解析器
         
///</summary>
         
///<param name="s">带格式字符串</param>
         
///<returns></returns>

         public ArrayList Parser(string s)
         
{
              ArrayList list 
= new ArrayList();
 
              
string soure = s;
 
              
//表达式解析
              string regStr = @"/[/$(?<eval>.*?)/$/]";
              Regex r 
= new Regex(regStr);
              MatchCollection mc 
= r.Matches(soure); 
 
              
foreach (Match m in mc) 
              
{
                   
//表达式
                   string eval = string.Format("[${0}$]",m.Groups["eval"].Value);
 
                   list.Add(eval);
              }

 
              
return list;
         }

 
         
///<summary>
         
/// 解析器
         
///</summary>
         
///<param name="s">带格式字符串</param>
         
///<returns></returns>

         public ArrayList ParserClient(string s)
         
{
              ArrayList list 
= new ArrayList();
 
              
string soure = s;
 
              
//表达式解析
              string regStr = @"/[/%(?<eval>.*?)/%/]";
              Regex r 
= new Regex(regStr);
              MatchCollection mc 
= r.Matches(soure); 
 
              
foreach (Match m in mc) 
              
{
                   
//表达式
                   string eval = m.Groups["eval"].Value;
 
                   list.Add(eval);
              }

 
              
return list;
         }

 
 
         
///<summary>
         
/// 取得参数字符串中的值get(key)
         
///</summary>
         
///<param name="eval">参数字符串</param>
         
///<returns>key</returns>

         public string GetKey(string eval,string Name)
         
{
              Regex rg 
= new Regex(string.Format(@"{0}/((?<g>.*?)/)",Name));
              
              
if(!rg.IsMatch(eval))
                   
return null;
              
else
                   
return rg.Match(eval).Groups["g"].Value;       
         }

 
     }
//class
}
找一个hashtable用来传递报表的临时值。这样就不用关心数据的名称及出现的具体位置了。
using  System;
using  System.Xml;
using  System.Reflection;
using  System.Collections;
 
namespace  XReport
{
     
///<summary>
     
/// 报表数据
     
///</summary>

     public class ReportData
     
{
         
///<summary>
         
/// 利用hash表保存数据
         
///</summary>

         private Hashtable data;
 
         
///<summary>
         
/// 构造器
         
///</summary>

         public ReportData()
         
{
            data 
= new Hashtable();
         }

 
         
///<summary>
         
/// 添加一条数据
         
///</summary>
         
///<param name="key">关键字</param>
         
///<param name="o">对象</param>

         public void Add(string key,object o)
         
{
              data.Add(key,o);
         }

 
         
///<summary>
         
/// 删除一个对象
         
///</summary>
         
///<param name="key">关键字</param>

         public void Delete(string key)
         
{
              data.Remove(key);
         }

 
         
///<summary>
         
/// 取得报表数据
         
///</summary>
         
///<param name="element">xml节点(doc/data)</param>
         
///<param name="ReportID">报表id</param>
         
///<returns></returns>

         public static ReportData GetReportData(XmlNode element,string ReportID)
         
{
              
//该报表的数据管理类
              string dataClass = ReportElement.GetChildNode(element,"DataClass").InnerText;
              
string method = ReportElement.GetChildNode(element,"get").InnerText;
 
              Type t 
= Type.GetType(dataClass);
         
              
//数据方法
              BindingFlags flags = BindingFlags.InvokeMethod|BindingFlags.Instance|BindingFlags.Public|BindingFlags.Static;
         
              
//设置参数
              Object[] args = new Object[] {ReportID};
 
              
//调用方法,返回报表数据
              return (ReportData)t.InvokeMember(method,flags,null,null,args);
         }

 
         
///<summary>
         
/// 设置报表数据
         
///</summary>
         
///<param name="element">xml节点(doc/data)</param>
         
///<param name="ReportID">报表id</param>
         
///<returns></returns>

         public static object SetReportData(XmlNode element,ReportData data,string ReportID)
         
{
              
//该报表的数据管理类
              string dataClass = ReportElement.GetChildNode(element,"DataClass").InnerText;
              
string method = ReportElement.GetChildNode(element,"set").InnerText;
 
              Type t 
= Type.GetType(dataClass);
         
              
//数据方法
              BindingFlags flags = BindingFlags.InvokeMethod|BindingFlags.Instance|BindingFlags.Public|BindingFlags.Static;
         
              
//设置参数
              Object[] args = new Object[] {data,ReportID};
 
              
//调用方法,返回报表数据
              return t.InvokeMember(method,flags,null,null,args);
         }

 
         
///<summary>
         
/// 取得数据
         
///</summary>
         
///<param name="key">关键字</param>
         
///<returns></returns>

         public object Get(string key)
         
{
              
return data[key];
         }

     }

}

现在是呈现报表和保存报表数据的时候了
using  System;
using  System.Xml;
using  System.Text;
using  System.Web;
using  System.Web.UI;
using  System.Collections;
using  System.Web.UI.HtmlControls;
 
namespace  XReport
{
     
///<summary>
     
/// 报表文档
     
///</summary>

     public class ReportDocument
     
{
         
private XmlDocument doc ;
         
private HtmlControl _Canvas;
         
private string xmlPath;
         ReportData data;
 
         
///<summary>
         
/// 画布
         
///</summary>

         public HtmlControl Canvas
         
{
              
get{return _Canvas;}
              
set{_Canvas = value;}
         }

 
         
///<summary>
         
/// 构造器
         
///</summary>
         
///<param name="path">xml定义文件路径</param>

         public ReportDocument(string path)
         
{
              xmlPath 
= path;
              doc 
= new XmlDocument();
              doc.Load(path);
         }

 
         
初始化画布(用于容纳报表)
 
         
输出报表对象及报表所包含的值
 
         
读取报表返回的值
}
新建立一个webform,读取报表的时候只要用
            
  ReportDocument r  =   new  ReportDocument(Server.MapPath( " pp.xml " ));
              System.Web.UI.Control c 
=  r.WriteReport();
              Label1.Controls.Add(c);
将数据保存到数据库则调用             
  ReportDocument r  =   new  ReportDocument(Server.MapPath( " pp.xml " ));
              r.SaveReport();

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值