在项目中经常遇到一个问题,打印word或者打印excel的时候,我们经常使用一对一的赋值或者批量替换的方式来对模板进行修改。

但是现在遇到两种场景:

1、取值是通过自定以方法进行取值的。

如:一个销售单据,会涉及到很多种费用,并且这些费用是由后台配置的,非常灵活。但是我们在制作打印模板时取值各项费用我们该如何去定义他呢,如何给他赋值呢?我们如果针对这一个场景下的模板进行一个特殊定义后,在打印另一份单据或者遇到同样的取值非常灵活的数据,是不是也需要进行特殊处理呢。

2、取值是通过自行定义进行取值的。

如:还是一个销售单据,我们打印的可能是销售价格,成本、毛利,但是如果我们打印的时候涉及到提成配比,提成配比可能是根据销售价格算的,可能根据毛利算的,可能根据效益来算的,那么是不是我们在做这个模板的时候定义:提成(按成本)、提成(按毛利)、提成...。

在这中情况下,我的解决方案是采用反射与javascript进行处理:

这里大致讲述一下我的解决思路,各位过路大神,各位奋战一线的程序猿们,看过笑过,不喜勿喷~

第一步:建立两种Eval方法,来解析表达式

C#Eval反射式:(此种方式主要应对在程序中自定义的方法,根据参数及方法来模拟程序中的计算,并将结果返回过去,这种方法必须制定处理他的主体Object)

/// <summary>
/// CShrapEval 的摘要说明
/// </summary>
public class CShrapEval
{ /// <summary>
/// 计算结果,如果表达式出错则抛出异常
/// </summary>
public static object Eval(string action,Type type,object obj,object[] parm)
{
return type.InvokeMember(
action,
BindingFlags.InvokeMethod,
null,
obj,
parm
);
} public static object Eval(string Cstring, Type type, object obj)
{
string action = Cstring.Split('|')[0];
object[] parm = Cstring.Split('|')[1].Split(',');
return type.InvokeMember(
action,
BindingFlags.InvokeMethod,
null,
obj,
parm
);
}
}

  JavaScript脚本编译方式:模拟javascript工作方式去处理一个表示式,可以使用一个javascript常用函数(如getdate()  length等),灵活方便

/**/
/// <summary>
/// 动态求值
/// </summary>
public class JavaEval
{
/**/
/// <summary>
/// 计算结果,如果表达式出错则抛出异常
/// </summary>
/// <param name="statement">表达式,如"1+2+3+4"</param>
/// <returns>结果</returns>
public static object Eval(string statement)
{
return _evaluatorType.InvokeMember(
"Eval",
BindingFlags.InvokeMethod,
null,
_evaluator,
new object[] { statement }
);
}
/**/
/// <summary>
///
/// </summary>
static JavaEval()
{
//构造JScript的编译驱动代码
CodeDomProvider provider = CodeDomProvider.CreateProvider("JScript"); CompilerParameters parameters;
parameters = new CompilerParameters();
parameters.GenerateInMemory = true; CompilerResults results;
results = provider.CompileAssemblyFromSource(parameters, _jscriptSource); Assembly assembly = results.CompiledAssembly;
_evaluatorType = assembly.GetType("Evaluator"); _evaluator = Activator.CreateInstance(_evaluatorType);
} private static object _evaluator = null;
private static Type _evaluatorType = null;
/**/
/// <summary>
/// JScript代码
/// </summary>
private static readonly string _jscriptSource = @"class Evaluator
{
public function Eval(expr : String) : String
{
return eval(expr);
}
}";
}

  第二步、构建好两个eval之后我们就需要在程序中去识别那些是C#,那些是javascript代码断

这里我处理的办法是:<c ...代码  /> 和<J ...代码 />使用这两种方式分别标示是那种代码

然后在处理中我们只需要找出那些是C代码 那些是J代码,并且对代码断进行计算

       public void ExportDoc()
{
ExportReplace();
foreach (NodeTemplate temp in DocTemplateList)
{
ExportDoc(temp);
}
if (ActionObject != null)
{
//动态取值
ExportDymic();
}
} //定义C表达式
System.Text.RegularExpressions.Regex RegexC = new System.Text.RegularExpressions.Regex(@"\<C\w*\|\w*[\,\w*]*\\\>");
//定义J表达式
System.Text.RegularExpressions.Regex RegexJ = new System.Text.RegularExpressions.Regex(@"\<J^\>*\\\>"); //业务逻辑理论为先处理C在处理J,但是C与J由存在循环处理的过程
public void ExportDymic()
{
var MatchesS = RegexC.Matches(doc.GetText());
foreach (System.Text.RegularExpressions.Match MatchC in MatchesS)
{
string Cstring = MatchC.Value.Replace("<C", "").Replace("\\>", "");
string result = CEval(Cstring);
//CShrapEval.Eval(Cstring, this.GetType(), this).ToString();
//A = A.Replace(MatchC.Value, result);
doc.Range.Replace(MatchC.Value, result, false, false);
}
MatchesS = RegexJ.Matches(doc.GetText());
foreach (System.Text.RegularExpressions.Match MatchC in MatchesS)
{
string Jstring = MatchC.Value.Replace("<J", "").Replace("\\>", "");
string result = JavaEval.Eval(Jstring).ToString();
doc.Range.Replace(MatchC.Value, result, false, false);
} } public string CEval(string A)
{
var MatchesS = RegexC.Matches(A);
foreach (System.Text.RegularExpressions.Match MatchC in MatchesS)
{
string Cstring = MatchC.Value.Replace("<C", "").Replace("\\>", "");
string result = CEval(Cstring).ToString();
A = A.Replace(MatchC.Value, result);
}
MatchesS = RegexJ.Matches(A);
foreach (System.Text.RegularExpressions.Match MatchC in MatchesS)
{
string Jstring = MatchC.Value.Replace("<J", "").Replace("\\>", "");
string result = JEval(Jstring).ToString();
A = A.Replace(MatchC.Value, result);
} return CShrapEval.Eval(A, ActionObject.GetType(), ActionObject).ToString();
} public string JEval(string A)
{
var MatchesS = RegexC.Matches(A);
foreach (System.Text.RegularExpressions.Match MatchC in MatchesS)
{
string Cstring = MatchC.Value.Replace("<C", "").Replace("\\>", "");
string result = CEval(Cstring).ToString();
A = A.Replace(MatchC.Value, result);
}
MatchesS = RegexJ.Matches(A);
foreach (System.Text.RegularExpressions.Match MatchC in MatchesS)
{
string Jstring = MatchC.Value.Replace("<J", "").Replace("\\>", "");
string result = JEval(Jstring).ToString();
A = A.Replace(MatchC.Value, result);
}
return JavaEval.Eval(A).ToString();
}

  

这样就可以对表达进行精确的解析了,当然目前还有一些未考虑完全的地方 ,待各位看客老爷指点。

好的~今天就贴到这里, 后期看看被喷的程度来确定是否继续在博客园发一些日志

关于模板中的动态取值 ---反射与javascript脚本编译的更多相关文章

  1. mybatis中两种取值方式?谈谈Spring框架理解?

    1.mybatis中两种取值方式? 回答:Mybatis中取值方式有几种?各自区别是什么? Mybatis取值方式就是说在Mapper文件中获取service传过来的值的方法,总共有两种方式,通过 $ ...

  2. Vue 列动态取值

    在前端开发过程中,可能会遇到列动态取值的情况,即列表中某列的取值由两个或以上的字段的值决定. 用 Vue 实现的话可以用如下代码解决 <template slot-scope="sco ...

  3. C#中float的取值范围和精度

    原文:C#中float的取值范围和精度 float类型的表现形式: 默认情况下,赋值运算符右侧的实数被视为 double. 因此,应使用后缀 f 或 F 初始化浮点型变量,如以下示例中所示: floa ...

  4. JSP中EL表达式取值问题记录(已解决)

    ***************************2015-10-28 22:21************************* 问题描述如下: 在当前的jsp页面已经有了如下代码: < ...

  5. C语言中数据类型的取值范围

    C语言中数据类型的取值范围如下:char -128 ~ +127 (1 Byte)short -32767 ~ + 32768 (2 Bytes)unsigned short 0 ~ 65536 (2 ...

  6. loadrunner 脚本优化-参数化之场景中的参数化取值

    脚本优化-场景中的参数化取值 by:授客 QQ:1033553122   Action() { lr_eval_string("{NewParam}"); lr_eval_stri ...

  7. mysql中数据类型的取值范围

    mysql整型bigint.int.mediumint.smallint 和 tinyint的语法介绍,如下: 1.bigint 从 -2^63 (-9223372036854775808) 到 2^ ...

  8. Struts2中EL表达式取值

    http://blog.csdn.net/cuihaiyang/article/details/41950141 (写的不错,可以知道为什么struts2可以用El取属性值的问题.正常el从reque ...

  9. 基础学习:C#中float的取值范围和精度

    float类型的表现形式: 默认情况下,赋值运算符右侧的实数被视为 double. 因此,应使用后缀 f 或 F 初始化浮点型变量,如以下示例中所示: float x = 3.5F; 如果在以上声明中 ...

随机推荐

  1. cf C Milking cows

    题意:输入n,然后输入n个数,在n个数中0或1,0代表这头牛向左看,1代表这头牛向右看,问最后最少损失多少牛奶. 思路:贪心,连着的0可以不损失,一旦插入1就会损失牛奶. #include <c ...

  2. FindControl什么时候才会使用ObjectFromHWnd函数呢?——VCL很难调试,加一个日志函数,记录时间

    IsDelphiHandleFindVCLWindowfunction IsVCLControl(Handle: HWND): Boolean;function FindControl(Handle: ...

  3. Vi的几种退出方式

    1.q 退出 2.w 保存,继续操作 3.wq 保存退出 4.q! 不保存,放弃修改 5.x 同wq相似,但又有区别 wq   强制性写入文件并退出.即使文件没有被修改也强制写入,并更新文件的修改时间 ...

  4. 【转】 linux iio子系统

    原文网址:http://blog.csdn.net/tsy20100200/article/details/47101661 最近由于工作的需要,接触了Linux iio子系统,对于这个目录其实以前是 ...

  5. hihoCoder 1389 Sewage Treatment 【二分+网络流+优化】 (ACM-ICPC国际大学生程序设计竞赛北京赛区(2016)网络赛)

    #1389 : Sewage Treatment 时间限制:2000ms 单点时限:2000ms 内存限制:256MB 描述 After years of suffering, people coul ...

  6. 福州大学 Problem 2168 防守阵地 I

    http://acm.fzu.edu.cn/problem.php?pid=2168 最重要的是 dp[k]=dp[k-1]-ans[k-1]+x[i]*m; ans[k-1]是m个数求和.  Pro ...

  7. 差别不在英语水平,而在汉语水平If you do not leave me, we will die together.

    为什么高考语文要提高到180分,英语降到100,差别不在英语水平,而在汉语水平.看下面例句的译法: If you do not leave me, we will die together. 你如果不 ...

  8. 《A First Course in Probability》-chaper1-组合分析-方程整数解的个数

    在概率论问题中求解基本事件.某个事件的可能情况数要涉及到组合分析. 而这一部分主要涉及到简单的计数原理和二项式定理.多项式定理. 我们从一个简单的实例入手. 方程的整数解个数: Tom喜欢钓鱼,一直他 ...

  9. 简单的闭包运算(Closure)演示程序

    /* * 该程序用于计算某个产生式的闭包 * RexfieldVon * 2013年8月9日16:01:38 */ #include <stdio.h> #include <stdl ...

  10. SoupUI学习资料

    官网: https://www.soapui.org 下载地址: https://www.soapui.org/downloads/soapui.html 官方文档: https://www.soap ...