前台和后台数据传递综合总结
1.前台将Json字符串数据传递到后台进行处理
1
2
3
4
5
6
7
8
9
10
|
//var Json = '{"User":[{"UserName":"a","PassWord":"a"},{"UserName":"b","PassWord":"b"}]}';这是前台 string json = Request.Form[ "Json" ]; JObject o = (JObject)JsonConvert.DeserializeObject(json); JToken arr = (JToken)o[ "User" ]; //此地返回的是多条记录,所以要遍历 foreach ( var token in arr) { Person p = JsonConvert.DeserializeObject<Person>(token.ToString()); //在此对p进行增删改操作,p已经是一个实体类的实例 } Response.End(); |
对Json字符串进行后台处理,需要应用到程序集Newtonsoft.Json.dll
另外:如果是一条记录的Json,处理方式是:
1
2
3
4
5
6
7
|
//var Json = '{"UserName":"c","PassWord":"c"}';这是前台 string json = Request.Form[ "Json" ]; JObject o = (JObject)JsonConvert.DeserializeObject(json); JToken arr = (JToken)o; //此地返回的是一条记录,不进行选项选择都是一条,不用遍历 Person p = JsonConvert.DeserializeObject<Person>(arr.ToString()); //在此对p进行增删改操作,p已经是一个实体类的实例 Response.End(); |
2.组织对象,然后转换为Json字符串
1
2
3
4
5
|
var opts = { UserName: "d" , PassWord: "d" }; var json = jsonParseString(opts); |
方法jsonParseString 代码如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
//JSON对象转字符串 function jsonParseString(o) { try { if (o == undefined) { return "" ; } var r = []; if ( typeof o == "string" ) return "\"" + o.replace(/([\ "\\])/g, " \\$1 ").replace(/(\n)/g, " \\n ").replace(/(\r)/g, " \\r ").replace(/(\t)/g, " \\t ") + " \ "" ; if ( typeof o == "object" ) { if (!o.sort) { for ( var i in o) r.push( "\"" + i + "\":" + jsonParseString(o[i])); if (!!document.all && !/^\n? function \s*toString\(\)\s*\{\n?\s*\[native code\]\n?\s*\}\n?\s*$/.test(o.toString)) { r.push( "toString:" + o.toString.toString()); } r = "{" + r.join() + "}" } else { for ( var l = 0; l < o.length; l++) r.push(jsonParseString(o[l])) r = "[" + r.join() + "]" ; } return r; } return o.toString().replace(/\ "\:/g, '" : "" '); } catch (e) { return e.Message; } } |
最终生成的json字符串为:var Json = '{"UserName":"d","PassWord":"d"}',满足传递到后台的需求
3.前台直接传递到自己的后台,添加Response.End();可以防止传递HTML到前台
4.后台将数据转换为Json传递到前台
这是后台代码:
1
2
3
4
5
6
7
8
9
10
11
12
|
protected void Page_Load( object sender, EventArgs e) { if (Request.Form[ "Type" ] == "GetJson" ) { Person p = new Person(); p.UserName = "e" ; p.PassWord = "e" ; string json = JsonConvert.SerializeObject(p); //此处将对象转换为Json了 Response.Write(json); Response.End(); } } |
注意还可以将对象集合转换为Json,实例代码:
1
2
3
4
5
6
7
8
9
|
List<Person> persons = new List<Person>(); Person p = new Person(); p.UserName = "f" ; p.PassWord = "f" ; persons.Add(p); persons.Add(p); string json = JsonConvert.SerializeObject(persons); //此处将对象转换为Json了 内容为 "[{\"UserName\":\"f\",\"PassWord\":\"f\"},{\"UserName\":\"f\",\"PassWord\":\"f\"}]" Response.Write(json); Response.End(); |
同时他还可以将DataTable转换为Json字符串,实例代码如下:
1
2
3
4
5
6
7
8
9
10
|
protected void Page_Load( object sender, EventArgs e) { if (Request.Form[ "Type" ] == "GetJson" ) { DataTable dt = ZXGA.Utility.DbHelperOleDb.Query(App_Init.ConnStr(), "select * from jjdwb" ).Tables[0]; string json = JsonConvert.SerializeObject(dt); Response.Write(json); Response.End(); } } |
下面是前台代码:
1
2
3
4
5
|
$.post( "test2.aspx" , { Type: "GetJson" }, function (data) { //var obj = $.parseJSON(data);//方法一 var obj = eval( '(' + data + ')' ); //方法二 alert(obj.UserName); }); |
从Google开源RE2库学习到的C++测试方案
最近因为科研需求,一直在研究Google的开源RE2库(正则表达式识别库),库源码体积庞大,用C++写的,对于我这个以前专供Java的人来说真的是一件很痛苦的事,每天只能啃一点点。今天研究了下里面用到的测试方法,感觉挺好的,拿来跟大家分享下!(哈~C++大牛勿喷)
对于我这个C++菜鸟中的菜鸟而言,平时写几个函数想要测试一般都是在main中一个一个的测试,因为没用C++写过项目,没有N多方法所以在main中一个个测试也不费劲。但是对于一个项目而言,或多或少都有N多方法,如果在main中一个个测试的话,不仅效率低而且还容易出错遗漏什么的。那么该怎么进行测试呢?貌似现在有很多C++自动化测试的工具,反正我是一个没用过,也没法评价。我就说下Google在RE2库里是怎么测试的吧。
先用一个超级简单的例子来做讲解:测试两个方法getAsciiNum()和getNonAsciiNum(),分别求flow中ASCII码字符的数目和非ASCII码字符的数目。
第一步:写个头文件,定义测试所用类和测试方法。
// test.h
#define TEST(x, y) \ void x##y(void); \ TestRegisterer r##x##y(x##y, # x "." # y); \ void x##y(void) void RegisterTest(void (*)(void), const char*); class TestRegisterer { public: TestRegisterer(void (*fn)(void), const char *s) { RegisterTest(fn, s); } };
解析:首先看定义的类TestRegisterer,有个构造方法,两个参数:
1. 一个函数指针:void (*fn)(void),指向我们具体要编写的测试方法名;
2. 一个字符串:const char *s,属于该测试方法的描述信息。
这个构造函数调用了另一个函数RegisterTest(),具体实现见下面。
然后看最上面定义的宏TEST(x, y),主要将其替换为TestRegisterer r##x##y(x##y, # x "." # y);其中x##y作为方法名,# x "." # y作为描述信息。这里可能有些和我一样入门级别的人没怎么看懂这个宏,因为不知道前后加void x##y(void);这个是干嘛用的?一开始我也没想明白,因为不加的话就会报错,后来通过gcc的-E选项激活宏编译,看了下编译期间展开成啥模样了。这里以一个简单的例子作为说明:假设x为test,y为flow,如果不加前后那个,那么展开后为TestRegisterer rtestflow(testflow, "test.flow"); 这明显是个函数声明,有两个参数,第二个是字符串,那么第一个是什么?编译器会认为是个函数名(实际上也是的),但这个函数前面明显未定义,就会报找不到此函数声明的错误,所以就需要在之前加上void x##y(void);声明函数,当然光声明不实现在链接时同样报错,所以就需要在之后加上void x##y(void)进行具体实现了,注意这里没有逗号,也没有具体实现的{},因为这只是宏,Google的所有测试函数是这样写的:
TEST(x, y) { .... // 具体实现 }
那么上面例子TEST(test, flow){ ... // 具体实现 },整体展开后就是这样:
void testflow(void); TestRegisterer rtestflow(testflow, "test.flow"); void testflow(void) { .... // 具体实现 }
第二步:N多个具体的测试实现。
#include <string> #include <vector> #include "test.h" #define arraysize(array) (sizeof(array)/sizeof((array)[0])) #define CHECK_EQ(x, y) if((x) != (y)) { printf("test failed!\n"); system("pause"); exit(0); } struct TestFlow { const char* flow; const int num; }; static struct TestFlow tests1[] = { {"\x02\x97\xa4\xe6\xfe\x0c", 2}, {"\x05\x97\x35\xe6\xfe\xac\x04", 3}, {"\xb2\x97\xa5\xe6\x9c\x1c\x58\xaa\x97\x03", 3}, {"\x32\x97\xa5\x05\x9c\xac\xe8\xaa\x57", 3}, {"\x42\x01\xa5\x86\x0c\x56\xe8\xaa\x97\x03", 5}, }; static struct TestFlow tests2[] = { {"\x02\x97\xa4\xe6\xfe\x0c", 4}, {"\x05\x97\x35\xe6\xfe\xac\x04", 4}, {"\xb2\x97\xa5\xe6\x9c\x1c\x58\xaa\x97\x03", 7}, {"\x32\x97\xa5\x05\x9c\xac\xe8\xaa\x57", 6}, {"\x42\x01\xa5\x86\x0c\x56\xe8\xaa\x97\x03", 5}, }; int getAsciiNum(const char*); int getNonAsciiNum(const char*); TEST(TestAsciiNum, Simple) { int failed = 0; for (int i = 0; i < arraysize(tests1); i++) { const TestFlow& t = tests1[i]; int num = getAsciiNum(t.flow); if (num != t.num) { failed++; } } CHECK_EQ(failed, 0); } TEST(TestNonAsciiNum, Simple) { int failed = 0; for (int i = 0; i < arraysize(tests2); i++) { const TestFlow& t = tests2[i]; int num = getNonAsciiNum(t.flow); if (num != t.num) { failed++; } } CHECK_EQ(failed, 0); } int getAsciiNum(const char* flow) { // we assume that there's no \x00 in flow otherwise we cannot use strlen() int num = 0, i; for(i = 0; i < strlen(flow); i++) { // ASCII: 0 ~ 127 if(flow[i] >= 0 && flow[i] < 128) num++; } return num; } int getNonAsciiNum(const char* flow) { // we assume that there's no \x00 in flow otherwise we cannot use strlen() int num = 0, i; for(i = 0; i < strlen(flow); i++) { // ASCII: 0 ~ 127 if(flow[i] < 0 || flow[i] >= 128) num++; } return num; }
看上去一目了然,TEST(TestAsciiNum, Simple)和TEST(TestNonAsciiNum, Simple)就是两个具体的测试实现了,这个例子很简单,仅仅是为了说明问题。
第三步:具体的测试方案。
// test.cpp #include <stdio.h> #include <stdlib.h> #include "test.h" struct Test { void (*fn)(void); const char *name; }; static Test tests[10000]; static int ntests; void RegisterTest(void (*fn)(void), const char *name) { tests[ntests].fn = fn; tests[ntests++].name = name; } int main(int argc, char **argv) { for (int i = 0; i < ntests; i++) { printf("%s\n", tests[i].name); tests[i].fn(); } printf("PASS\n"); system("pause"); return 0; }
解析:
1. 结构体Test存储具体的测试实现,定义最多能有10000个不同的方法测试,也就是能同时测试10000个方法。
2. ntests代表实际所测试的方法数,我这里就是2了。
3. RegisterTest()具体的实现也比较简单,就是将实际所要测试的方法名和描述信息存储到Test结构体数组tests中。
4. 最后就是在main中进行统一测试了,首先输出测试方法描述信息,以便知道当前测试了哪些方法及如果有测试失败时能及时进行排查。然后就是具体的执行测试函数了。
本例的测试结果如下:
思考:下面看下具体是如何执行的:
大家可能觉得main写的太简洁,一开始什么都没调用,直接来个for循环,ntests的值初始不是0吗?在main一开始也没显式的调用RegisterTest()将测试方法加进去啊,怎么一进入main,ntests就变成2了?
大家要记住:所有的测试具体实现都是在TEST这个宏里面,而宏是在编译期间就开始展开了。以 TEST(TestAsciiNum, Simple){ ... }为例,具体的执行过程如下:
编译期间:
TEST(TestAsciiNum, Simple)展开为:
void TestAsciiNumSimple(void); TestRegisterer rTestAsciiNumSimple(TestAsciiNumSimple, "TestAsciiNum.Simple"); void TestAsciiNumSimple(void) { int failed = 0; for (int i = 0; i < arraysize(tests1); i++) { const TestFlow& t = tests1[i]; int num = getAsciiNum(t.flow); if (num != t.num) { failed++; } } CHECK_EQ(failed, 0); }
然后就触发调用了TestRegisterer的构造方法从而开始执行RegisterTest(TestAsciiNumSimple, "TestAsciiNum.Simple")方法,将TestAsciiNumSimple方法名和描述信息"TestAsciiNum.Simple"加入到结构体数组tests中,这时ntests增为1,同理另一个宏TEST(TestNonAsciiNum, Simple)展开后也将TestAsciiNonNumSimple方法名和描述信息"TestNonAsciiNum.Simple"加入到结构体数组tests中,这时ntests增为2,这是编译期间做的事。
运行期间:
从main开始,执行for循环,先后执行了具体的测试实现方法TestAsciiNumSimple()和TestAsciiNonNumSimple()从而完成测试。
用一个图来说明更加清晰(图画的不太好,望见谅~~~)