Blue Boy

Blue的Me,有着Blue的idea,向往Blue的Dream,追求Blue的U!
posts - 2, comments - 2, trackbacks - 0, articles - 0
  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理

2011年6月12日

      由于开发需要,想实现一些提示信息的通用,比如,提示信息内容:“XXX的值YYY不正确,请检查并重新输入!”,那么,通用的信息内容就应该为:“{0}的值{1}不正确,请检查并重新输入!”。但这个“{0}”和"{1}"怎么替换掉呢?在.net中,字符串有String.Format函数,这样我们就可以很容易地去根据不同的内容,替换成不一样的内容,但Oracle里面还没有样的函数(据个人所知,不知道有没有孤漏寡闻!),但又想用,所以就只能自己写了。

      想了想,有问题啊,且听我道来:

      信息内容中有多个参数,并且都是用花括号中带数字来表示,如{0},{1},{2},那么,这些参数最好能用数组来表示,但Oracle PL/SQL中有数组吗?都怪自己平时不努力,书到用时方恨少啊。在网上百谷了一下,Oralce里面没有现成的数据类型,只有通过自定义数组类型(其实是Table表结构类型).如下代码所示: 

String Array Type Defintion
1 CREATE OR REPLACE TYPE stringarrary IS TABLE OF VARCHAR2 (4000);

 

      可是问题又来了,用的时候,怎么传值给函数啊?杯具了,纠结了好一会,算了,还是笨一会吧,字符串分割好了。那么这个函数需要什么东东呢?

      1.信息内容

      2.参数列表(多个参数用分割符分开)

      3.分割符(当然是跟2中一致的,不然,可就牛头不对马嘴了)

      开始写吧,转头一想,是不是分成两个函数会更好呢?一个用于分割返回参数数组列表,另一个专就是调用分割函数得到参数列表后,做替换的动作,最后返回。en,想想不错,就写成两个吧,功能很单纯,很天真。

      俗话说,他山之石,可以攻玉。百谷了一下,分割函数有现成的,拿来吧,请往下看:

String Split function Definition
 1 CREATE OR REPLACE FUNCTION stringsplit (SOURCE VARCHAR2, spliter VARCHAR2)
 2    RETURN stringarrary
 3 IS
 4    j             INT             := 0;
 5    i             INT             := 1;
 6    len           INT             := 0;
 7    len1          INT             := 0;
 8    str           VARCHAR2 (4000);
 9    returnvalue   stringarrary    := stringarrary ();
10 BEGIN
11    IF (spliter IS NULL) OR (SOURCE IS NULL)
12    THEN
13       returnvalue.EXTEND;
14       returnvalue (1) := SOURCE;
15    ELSE
16       len := LENGTH (SOURCE);
17       len1 := LENGTH (spliter);
18 
19       WHILE j < len
20       LOOP
21          j := INSTR (SOURCE, spliter, i);
22 
23          IF j = 0
24          THEN
25             j := len;
26             str := SUBSTR (SOURCE, i);
27             returnvalue.EXTEND;
28             returnvalue (returnvalue.COUNT) := str;
29 
30             IF i >= len
31             THEN
32                EXIT;
33             END IF;
34          ELSE
35             str := SUBSTR (SOURCE, i, j - i);
36             i := j + len1;
37             returnvalue.EXTEND;
38             returnvalue (returnvalue.COUNT) := str;
39          END IF;
40       END LOOP;
41    END IF;
42 
43    RETURN returnvalue;
44 END stringsplit;
   

那就剩下外面的皮了,这个雕琢一下,也出来了:

String Replace Function Definition
1 CREATE OR REPLACE FUNCTION stringreplace (
2 SOURCE VARCHAR2,
3 param VARCHAR2,
4 spliter VARCHAR2
5 )
6 RETURN VARCHAR2
7 IS
8 i INT := 0;
9 len INT := 0;
10 params stringarrary := stringarrary ();
11 returnvalue VARCHAR2 (4000);
12 BEGIN
13 params := stringsplit (param, spliter);
14
15 IF params.COUNT > 0
16 THEN
17 len := params.COUNT;
18 returnvalue := SOURCE;
19
20 WHILE i < len
21 LOOP
22 returnvalue :=
23 REPLACE (returnvalue,
24 CONCAT (CONCAT ('{', TO_CHAR (i)), '}'),
25 params (i + 1)
26 );
27 i := i + 1;
28 END LOOP;
29 END IF;
30
31 RETURN returnvalue;
32 END stringreplace;

当然写完要测试下:

      1.分割函数测试如下:      

stringsplit test code
1 SELECT *
2   FROM TABLE (CAST (stringsplit ('1|12|123|1234||12345|''|'AS stringarrary));

        结果:    

               COLUMN_VALUE
               1
               12
               123
               1234
    
               12345

        还不赖。

      2.测试外包函数了。

 

stringreplace test
1 select stringreplace('{0}+{1}={0}{1}','吱吱|YY','|'from dual;

        结果:

          STRINGREPLACE('{0}+{1}={0}{1}','吱吱|YY','|')
          吱吱+YY=吱吱YY

        唔,自我感觉还不错。

       完了,小某小才,欢迎各位拍砖,如果有更好的做法,望不吝指导,谢谢!

 

 

备注:Oralce PL/SQL中字符串的空怎么判断?

       一定要用   IS NULL ,不能用 =''  哦!!!!!

posted @ 2011-06-12 02:31 Blue Boy 阅读(787) 评论(2) 编辑

2010年6月18日

     样例测试代码下载:ReportVeiwer实现多语言样例

  由于使用了MS的ReportViewer来做报表,项目上线又会跨地区,故多语言的问题在所难免。

对于ReportViewer的多语言在网上已有不少文章,这里所描述的也是参考网上的一些思路而成。

现在,我所知道的对于ReportViewer的多语言的解决方案只有三种:

1.通过编写多语言的RDLC(ReportViewer所承载的报表定义文件),然后根据不同的语言编程实现加载不同语言的报表定义文件,如Report.ZH-CN.rdlc等。

  【缺点:后续需要修改报表的情况下,需要同步修改多份报表定义,增加维护的难度】

2.通过把所有在Report中显示的所有标识类文本,都提出来作为参数的方式传递给报表,这样传进来的参数的值实现多语言就行了。

  【缺点:增加编写报表定义和编程的一些难度,参数列表会比较冗繁,维护难度也有所增加】

3.通过修改报表定义文档内容后,再通过ReportViewer动态加载报表定义来实现多语言。

  【缺点:修改报表定义是通过把RDLC当作XML文档来读取并修改相应的XMLNode节点内容,所以在性能方面有所损耗】

对于1,2和3,这三种解决方案,对于熟悉ReportViewer报表的朋友来说,前1和2的方案基本上是毫无问题,测试例子也相当容易,对于3的方案,则有些陌生。这里,我也是为了记录3方案所做的测试和例子进行标记。

  通过利用ReportViewer.LocalReport.LoadReportDefinition()函数,动态加载报表定义,该函数支持从MemoryStream中加载报表定义,同时RDLC文件是标准的XML文档.

  至于我们如何定义在报表中需要进行多语言转换的标识文本呢,我们知道RDLC中的TEXTBOX文本会有一个Name的属性,我们会根据Name属性和Resource中的Key的值对应起来,这样我们就很清晰明确地知道哪些文本是需要转换成多语言的。

  【建议:对于存储在Resource中的信息,最好跟Report的ID关联起来,如Key的ID取名为ReportName_lbl0001,这样我们就知道ReportName中的lbl0001对应于Resource文档中的哪一个值。】

RDLC读取与ReportViewer动态加载报表定义
            Dim ms As New MemoryStream
            
Dim xmldoc As New XmlDocument
            
'对XML文档的方式读取RDLC文档
            xmldoc.Load(Server.MapPath("~"+ "/Report.rdlc")
            
Dim hashTran As New Hashtable
            
'RDLC控件的Name和Resource的Key值对
            hashTran("textbox3"= "V0001"
            hashTran(
"textbox4"= "V0002"
            hashTran(
"textbox5"= "V0003"
            hashTran(
"textbox1"= "V0004"
            hashTran(
"textbox2"= "V0005"
            
'遍历XML文档中的节点和转换成多语言的值
            TraversalXMLNodes2Translate(xmldoc, hashTran, "ReportResource")
            xmldoc.Save(ms)
            ms.Position 
= 0
            
Me.ReportViewer1.LocalReport.ReportPath = String.Empty
            
'动态加载报表定义
            Me.ReportViewer1.LocalReport.LoadReportDefinition(ms)

 

TraversalXMLNodes2Translate
1  Private Sub TraversalXMLNodes2Translate(ByRef pXmlDoc As XmlDocument, ByVal pHash As Hashtable, ByRef pResourceFile As String)
2         For Each node As XmlNode In pXmlDoc.DocumentElement.ChildNodes
3             If node.HasChildNodes Then
4                 TraversalXMLChildrenNodes2Translate(node, pHash, pResourceFile)
5             End If
6         Next
7     End Sub

 

TraversalXMLChildrenNodes2Translate
    Private Sub TraversalXMLChildrenNodes2Translate(ByRef pNode As XmlNode, ByRef pHash As Hashtable, ByRef pResourceFile As String)
        
Try
            
Dim strValue As String = String.Empty
            
If TypeOf (pNode) Is XmlElement And pNode.Name.ToUpper.Equals("TEXTBOX"Then
                strValue 
= pNode.Attributes("Name").Value
                
For Each node As XmlNode In pNode.ChildNodes
                    
If node.Name.ToUpper.Equals("VALUE"Then
                        
If node.InnerText.Trim.Length > 0 _
                        
AndAlso node.InnerText(0<> "=" _
                        
AndAlso pHash IsNot Nothing _
                        
AndAlso pHash.Contains(strValue) Then
                            strValue 
= GetValueFromResourceFile(pResourceFile, pHash(strValue))
                            
If Not String.IsNullOrEmpty(strValue) Then
                                node.InnerText 
= strValue
                            
End If
                        
End If
                        
Exit For
                    
End If
                
Next
            
ElseIf pNode.HasChildNodes Then
                
For Each cNode As XmlNode In pNode.ChildNodes
                    TraversalXMLChildrenNodes2Translate(cNode, pHash, pResourceFile)
                
Next
            
End If
        
Catch ex As Exception

        
End Try
    
End Sub

 

GetValueFromResourceFile
    Private Function GetValueFromResourceFile(ByRef pResourceFile As StringByVal key As StringAs String
        
Try
            
Return CStr(GetGlobalResourceObject(pResourceFile, key))
        
Catch ex As Exception
            
Return String.Empty
        
End Try
    
End Function

 

posted @ 2010-06-18 23:42 Blue Boy 阅读(186) 评论(0) 编辑