在項(xiàng)目中經(jīng)常遇到一個(gè)問(wèn)題,打印Word或者打印Excel的時(shí)候,我們經(jīng)常使用一對(duì)一的賦值或者批量替換的方式來(lái)對(duì)模板進(jìn)行修改。
但是現(xiàn)在遇到兩種場(chǎng)景:
1、取值是通過(guò)自定以方法進(jìn)行取值的。
如:一個(gè)銷售單據(jù),會(huì)涉及到很多種費(fèi)用,并且這些費(fèi)用是由后臺(tái)配置的,非常靈活。但是我們?cè)谥谱鞔蛴∧0鍟r(shí)取值各項(xiàng)費(fèi)用我們?cè)撊绾稳ザx他呢,如何給他賦值呢?我們?nèi)绻槍?duì)這一個(gè)場(chǎng)景下的模板進(jìn)行一個(gè)特殊定義后,在打印另一份單據(jù)或者遇到同樣的取值非常靈活的數(shù)據(jù),是不是也需要進(jìn)行特殊處理呢。
2、取值是通過(guò)自行定義進(jìn)行取值的。
如:還是一個(gè)銷售單據(jù),我們打印的可能是銷售價(jià)格,成本、毛利,但是如果我們打印的時(shí)候涉及到提成配比,提成配比可能是根據(jù)銷售價(jià)格算的,可能根據(jù)毛利算的,可能根據(jù)效益來(lái)算的,那么是不是我們?cè)谧鲞@個(gè)模板的時(shí)候定義:提成(按成本)、提成(按毛利)、提成...。
在這中情況下,我的解決方案是采用反射與Javascript進(jìn)行處理:
這里大致講述一下我的解決思路,各位過(guò)路大神,各位奮戰(zhàn)一線的程序猿們,看過(guò)笑過(guò),不喜勿噴~
第一步:建立兩種Eval方法,來(lái)解析表達(dá)式
C#Eval反射式:(此種方式主要應(yīng)對(duì)在程序中自定義的方法,根據(jù)參數(shù)及方法來(lái)模擬程序中的計(jì)算,并將結(jié)果返回過(guò)去,這種方法必須制定處理他的主體Object)
/// <summary>/// CShrapEval 的摘要說(shuō)明/// </summary>public class CShrapEval{ /// <summary> /// 計(jì)算結(jié)果,如果表達(dá)式出錯(cuò)則拋出異常 /// </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工作方式去處理一個(gè)表示式,可以使用一個(gè)javascript常用函數(shù)(如getdate() length等),靈活方便
/**//// <summary>/// 動(dòng)態(tài)求值/// </summary>public class JavaEval{ /**/ /// <summary> /// 計(jì)算結(jié)果,如果表達(dá)式出錯(cuò)則拋出異常 /// </summary> /// <param name="statement">表達(dá)式,如"1+2+3+4"</param> /// <returns>結(jié)果</returns> public static object Eval(string statement) { return _evaluatorType.InvokeMember( "Eval", BindingFlags.InvokeMethod, null, _evaluator, new object[] { statement } ); } /**/ /// <summary> /// /// </summary> static JavaEval() { //構(gòu)造JScript的編譯驅(qū)動(dòng)代碼 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); } }";}
第二步、構(gòu)建好兩個(gè)eval之后我們就需要在程序中去識(shí)別那些是C#,那些是javascript代碼斷
這里我處理的辦法是:<c...代碼 /> 和<J ...代碼 />使用這兩種方式分別標(biāo)示是那種代碼
然后在處理中我們只需要找出那些是C代碼 那些是J代碼,并且對(duì)代碼斷進(jìn)行計(jì)算
public void ExportDoc() { ExportReplace(); foreach (NodeTemplate temp in DocTemplateList) { ExportDoc(temp); } if (ActionObject != null) { //動(dòng)態(tài)取值 ExportDymic(); } } //定義C表達(dá)式 System.Text.RegularExpressions.Regex RegexC = new System.Text.RegularExpressions.Regex(@"/<C/w*/|/w*[/,/w*]*///>"); //定義J表達(dá)式 System.Text.RegularExpressions.Regex RegexJ = new System.Text.RegularExpressions.Regex(@"/<J^/>*///>"); //業(yè)務(wù)邏輯理論為先處理C在處理J,但是C與J由存在循環(huán)處理的過(guò)程 public void ExportDymic() { var MatchesS = RegexC.Matches(doc.GetText()); foreach (System.Text.RegularExpressions.Match MatchC in MatchesS) { string Cstring = MatchC.Value.Replace("<C", "").Replace("http://>", ""); 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("http://>", ""); 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("http://>", ""); 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("http://>", ""); 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("http://>", ""); 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("http://>", ""); string result = JEval(Jstring).ToString(); A = A.Replace(MatchC.Value, result); } return JavaEval.Eval(A).ToString(); }
這樣就可以對(duì)表達(dá)進(jìn)行精確的解析了,當(dāng)然目前還有一些未考慮完全的地方 ,待各位看客老爺指點(diǎn)。
好的~今天就貼到這里, 后期看看被噴的程度來(lái)確定是否繼續(xù)在博客園發(fā)一些日志
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注