奥门泥斯人JSON序列化的归纳整理

by admin on 2019年9月2日

JSON定义

JSON(JavaScript Object Notation)
是一种轻量级的数据交换格式,易于阅读和编写,同时也易于机器解析和生成。它基于ECMA262语言规范(1999-12第三版)中JavaScript编程语言的一个子集。
JSON采用与编程语言无关的文本格式,但是也使用了类C语言(包括C, C++,
C#, Java, JavaScript, Perl,
Python等)的习惯,这些特性使JSON成为理想的数据交换格式。

JSON的结构基于下面两点:

  1. 名称/值”对的集合不同语言中,它被理解为对象(object),记录(record),结构(struct),字典(dictionary),哈希表(hash
    table),键列表(keyed list)等 ;
  2. 值的有序列表 多数语言中被理解为数组(array) ;

Dapper的简单使用(初学者归纳)

//引用:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;
using Model;
using Dapper;

//建立数据库连接
public static SqlConnection sqlconn()
{
string ConnString =
ConfigurationManager.ConnectionStrings[“ConnString”].ConnectionString;
var conntion = new SqlConnection(ConnString);
conntion.Open();
return conntion;
}

//列表
public List<User> GetList()
{
using (IDbConnection con = UserDal.sqlconn())
{
string sqlcommandtex = “select * from tb_User”;
List<User> userlist = SqlMapper.Query<User>(con,
sqlcommandtex).ToList();
return userlist;
}
}

//删除
public int del(int id)
{
using (IDbConnection con = UserDal.sqlconn())
{
string comm = “delete from tb_User where id = @id”;

int res = con.Execute(comm, new { id = id });
return res;
}
}

//参数列表
public User GetUser(int id)
{
using(IDbConnection con = UserDal.sqlconn())
{
string sql = “select * from tb_User where id=@Id”;
User q = con.Query<User>(sql, new { id = id
}).SingleOrDefault();
return q;
}
}

//修改
public int Upd(User qq)
{
using(IDbConnection con = UserDal.sqlconn())
{
string sql = “update tb_User set Name=@Name,Pwd=@Pwd,Age=@Age where
Id=@Id”;
int q = con.Execute(sql, qq);
return q;
}
}

Dapper的简单使用

//引用:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;
using Model;
using Dapper;

//建立数据库连接
public static SqlConnection sqlconn()
{
string ConnString =
ConfigurationManager.ConnectionStrings[“ConnString”].ConnectionString;
var conntion = new SqlConnection(ConnString);
conntion.Open();
return conntion;
}

//列表
public List<User> GetList()
{
using (IDbConnection con = UserDal.sqlconn
{
string sqlcommandtex = “select * from tb_User”;
List<User> userlist = SqlMapper.Query<User>(con,
sqlcommandtex).ToList();
return userlist;
}
}

//删除
public int del
{
using (IDbConnection con = UserDal.sqlconn
{
string comm = “delete from tb_User where id = @id”;

int res = con.Execute(comm, new { id = id });
return res;
}
}

//参数列表
public User GetUser
{
using(IDbConnection con = UserDal.sqlconn
{
string sql = “select * from tb_User where id=@Id”;
User q = con.Query<User>(sql, new { id = id
}).SingleOrDefault();
return q;
}
}

//修改
public int Upd
{
using(IDbConnection con = UserDal.sqlconn
{
string sql = “update tb_User set Name=@Name,Pwd=@Pwd,Age=@Age where
Id=@Id”;
int q = con.Execute;
return q;
}
}

JSON的序列化

JSON.NET使用简单说明,json.net使用简单

NewtonJson算是比较常用的json解析库了,我的项目中基本都使用该库,因为Unity上也有其相关的库,所以保证了多种项目之间的统一。同时NewtonJson的序列化和反序列化的接口比较简单,相对的功能也比较强大。不过在使用中也不是没有坑的,所以把一些心得记录下,以备日后查询。

JSON使用

JSON以一种特定的字符串形式来表示 JavaScript
对象。如果将具有这样一种形式的字符串赋给任意一个 JavaScript
变量,那么该变量会变成一个对象引用,而这个对象就是字符串所构建出来的,好像有点拗口,我们还是用实例来说明。这里假设我们需要创建一个User对象,并具有以下属性:用户ID、用户名、用户Email。您可以使用以下JSON形式来表示User对象:

{“UserID”:11, “Name”:”Truly”, “Email”:”zhuleipro◎hotmail.com”};

然后如果把这一字符串赋予一个JavaScript变量,那么就可以直接使用对象的任一属性了。完整代码:

<script>
var User = {"UserID":11, "Name":"Truly", "Email":"zhuleipro◎hotmail.com"};
alert(User.Name);
</script>  

实际使用时可能更复杂一点,比如我们为Name定义更详细的结构,使它具有FirstName和LastName:

{“UserID”:11, “Name”:{“FirstName”:”Truly”,”LastName”:”Zhu”},
“Email”:”zhuleipro◎hotmail.com”}

完整代码:

<script>
var User = {"UserID":11, "Name"{"FirstName":"Truly","LastName":"Zhu"}, 
 "Email":"zhuleipro◎hotmail.com"};
alert(User.Name.FirstName);
</script>  

现在我们增加一个新的需求,我们某个页面需要一个用户列表,而不仅仅是一个单一的用户信息,那么这里就需要创建一个用户列表数组。下面代码演示了使用JSON形式定义这个用户列表:

[
{"UserID":11, "Name":{"FirstName":"Truly","LastName":"Zhu"}, "Email":"zhuleipro◎hotmail.com"},
{"UserID":12, "Name":{"FirstName":"Jeffrey","LastName":"Richter"}, "Email":"xxx◎xxx.com"},
{"UserID":13, "Name":{"FirstName":"Scott","LastName":"Gu"}, "Email":"xxx2◎xxx2.com"}
]  

完整代码:

<script>
var UserList = [
{"UserID":11, "Name":{"FirstName":"Truly","LastName":"Zhu"}, "Email":"zhuleipro◎hotmail.com"},
{"UserID":12, "Name":{"FirstName":"Jeffrey","LastName":"Richter"}, "Email":"xxx◎xxx.com"},
{"UserID":13, "Name":{"FirstName":"Scott","LastName":"Gu"}, "Email":"xxx2◎xxx2.com"}
];
alert(UserList[0].Name.FirstName);
</script>  

事实上除了使用”.”引用属性外,我们还可以使用下面语句:

alert(UserList[0][“Name”][“FirstName”]); 或者
alert(UserList[0].Name[“FirstName”]);

现在读者应该对JSON的使用有点认识了,归纳为以下几点:

  1. 对象是属性、值对的集合。一个对象的开始于“{”,结束于“}”。每一个属性名和值间用“:”提示,属性间用“,”分隔。
  2. 数组是有顺序的值的集合。一个数组开始于”[“,结束于”]”,值之间用”,”分隔。
  3. 值可以是引号里的字符串、数字、true、false、null,也可以是对象或数组。这些结构都能嵌套。
  4. 字符串和数字的定义和C或Java基本一致。

本文通过一个实例演示,初步了解了JSON 的强大用途。可以归结如下:

  1. JSON 提供了一种优秀的面向对象的方法,以便将元数据缓存到客户机上。
  2. JSON 帮助分离了验证数据和逻辑。
  3. JSON 帮助为 Web 应用程序提供了 Ajax 的本质。

JSON(JavaScript Object Notation)
是一种轻量级的数据交换格式,易于阅读和编写,同时也易于机器解析和生成。它基于ECMA262语言规范(…

(一)序列化和反序列化

  • 序列化 : 将 字典或者数组等OC对象 转换成 二进制数据
    准备发送给服务器.
  • 反序列化 : 从服务器接收到 二进制数据 转换成
    字典或者数组等OC对象.

序列化和反序列化

序列化和反序列化很简单,调用相关的接口即可。反序列化的时候可以指定泛型参数,直接解析成对应的对象,这个功能比很多轻量级的JSON库要强很多了,省去了我们大量的new对象和赋值的步骤。也可以不指定泛型,解析成的对象可以转换成Dictionary,键值是字符串,value是object。

var data = new JsonData() {IntValue = 1000, FValue = 3.14f, IsTrue = true, Text = "Hello World"};
//序列化 json = "{\"IntValue\":1000,\"Text\":\"Hello World\",\"IsTrue\":true,\"FValue\":3.14}"
var json = JsonConvert.SerializeObject(data);

//反序列化
data = JsonConvert.DeserializeObject<JsonData>(json);

//不指定泛型的反序列化
var dict = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);

反序列化的时候,json键值对中的value,如果是整型,统一被转换成long,然后再进行转换,浮点型统一转换成Double,然后转换,string,bool就直接转换了。数组,list可以转换成JArray类型,然后再转换成我们需要的集合类型。

var dict = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
var new_data = new JsonData();
new_data.IntValue = (int)((long)dict["IntValue"]);
new_data.FValue = (float)((double)dict["FValue"]);
new_data.Text = (string)dict["Text"];
new_data.IsTrue = (bool)dict["IsTrue"];
new_data.array = ((JArray)dict["array"]).Values<int>().ToArray();

反序列化的时候,JSON.NET是将键值名和对象的成员名对应起来进行赋值的,它只认名称,不管顺序,甚至不管类,比如由以下两个类

class JsonData
{
    public int IntValue;
    public string Text;
    public bool IsTrue;
    public float FValue;
    public int[] array;
    public List<int> list;
}

class JsonDataB
{
    public bool IsTrue;
    public int IntValue;
    public float FValue;
    public int[] array;
    public List<int> list;
    public string Text;
}

var datab = JsonConvert.DeserializeObject<JsonDataB>(json);

同一段JSON可以解析成A也可以解析成B,这在一些项目中存在大量类似的类,或是相同类,处于不同的程序集中非常好用,节省大量的写反序列化的代码。假设Bname3,依然可以解析,并不会因为多了一个成员而无法解析,只是name3没有值而已。

(二)JSON序列化使用场景

  • 向服务器发送一些信息时,比如,发微博…

  • 我们可以将描述信息定义成JSON字符串,字典或者数组,再将其序列化成二进制形式的JSON字符串,发送给服务器,服务器接收到之后,可以直接反序列化成JSON字符串,字典或者数组.

  • 当我们需要向服务器发送一个OC对象时,可以将OC对象转换成二进制形式的JSON字符串.因为服务器不认识OC对象,不能直接发送.

指定反序列化的对象

如果我们想指定JSON字符串反序列化的对象,除了使用泛型参数,还可以使用自定义的Converter,继承JsonConverter后,在接受json字符串时,我们可以选择解析成A,或是B。换句话说,对一段JSON字符串指定一个new函数,但new什么,哪怕完全不相关的东西都可以,而json字符串只是new时的参数。

class DataConvert : JsonConverter
{
public override bool CanConvert(Type objectType)
{
    return true;
}

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
    var data = new JsonDataB();
    while(reader.Read())
    {
        if(reader.Value.ToString() == "IntValue")
        {
            data.IntValue = reader.ReadAsInt32().Value;
        }
    }
    return data;
}

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
    throw new NotImplementedException();
}
}

在使用泛型解析的时候,也可以对具体类的构造函数使用[JsonConstructor]特性来指定被反序列化使用的构造函数。在这里Json.Net中它会依次查找合适的构造函数,如果不指定该特性,就会先从默认的构造函数找起。

class JsonData
{
    public int IntValue;
    public string Text;
    public bool IsTrue;
    public float FValue;
    public int[] array;
    public List<int> list;

    public JsonData()
    {
    }

    [JsonConstructor]
    public JsonData(int intvalue, string text, bool istrue)
    {
        IntValue = intvalue * 2;            //改变
        Text = "We can do every thing we want here";
        FValue = 0.001f;                       //不改变
        //istrue丢失
    }
}

data = JsonConvert.DeserializeObject<JsonData>(json);

上面两个构造函数中,JSON.NET会在反序列化时执行第二个带参数的,而且两个参数值分别对应Json字符串中的值,如果形参的名字对应JSON字符串中的key值,而类型不对应,还会抛出异常。形参中没有包括的json的key-value字符串的值,会在构造后再次赋值。比如FValue就不会等于0.001,而是3.14

反序列化时,json.net是先将对象的字符串抽取出来,然后new出对象,并将这部分的json字符串传递给构造函数进行赋值,如果是默认构造函数,则会在new出后,进行赋值。这里一个问题是,如果使用了[JsonConstructor]指定了构造函数,而该函数是接受参数的,那么再new之后就不会再次赋值了,如果构造函数内没有对这个参数进行赋值,那这个值就丢失了。这个在我们使用时,就因为这个原因,造成总是丢失数据

(三)序列化字典和数组

多态反序列化

反序列化多态对象时,因为可能具体的类的成员比泛型参数来的多,想要正确反序列化的话,需要在序列化时,在JSON字符串中增加有效的类型信息。要继承SerializeBinder,该类的两个接口可以将类型转换成字符串,添加到json字符串中,反序列化时,通过拿到类型的字符串,使用反射来new出具体对象。

public class TypeNameSerializationBinder : ISerializationBinder
{

    public TypeNameSerializationBinder()
    {
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="serializedType">序列化的具体类型</param>
    /// <param name="assemblyName">写入json的程序集名</param>
    /// <param name="typeName">写入json的类型名</param>
    public void BindToName(Type serializedType, out string assemblyName, out string typeName)
    {
        assemblyName = "程序集";
        typeName = serializedType.FullName;
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="assemblyName">从json读入的程序集名</param>
    /// <param name="typeName">从json读入的类型名</param>
    /// <returns></returns>
    public Type BindToType(string assemblyName, string typeName)
    {

        var asm = AppDomain.CurrentDomain.GetAssemblies();
        foreach (var assembly in asm)
        {
            var types = assembly.GetTypes();
            foreach (var type in types)
            {
                if (type.FullName == typeName)
                {
                    return type;
                }
            }
        }

        return null;
    }
}

class clsBase
{
    public int num;
}

class subcls: clsBase
{
    public string txt;
}

class cls
{
    public clsBase m_base;
}
var c = new cls();
var b = new subcls() {num = 1001, txt = "I'm sub"};
c.m_base = b;
json = JsonConvert.SerializeObject(c);
var _c = JsonConvert.DeserializeObject<cls>(json);

//以下代码正确序列化
//json = "{\"m_base\":{\"$type\":\"JsonDotNetDemo.Program+subcls\",\"txt\":\"I'm sub\",\"num\":1001}}"
//json = "{\"m_base\":{\"$type\":\"JsonDotNetDemo.Program+subcls, 程序集\",\"txt\":\"I'm sub\",\"num\":1001}}"
json = JsonConvert.SerializeObject(c, new JsonSerializerSettings()
{
    TypeNameHandling = TypeNameHandling.Auto,
    SerializationBinder = new TypeNameSerializationBinder()
});
_c = JsonConvert.DeserializeObject<cls>(json, new JsonSerializerSettings()
{
    TypeNameHandling = TypeNameHandling.Auto,
    SerializationBinder = new TypeNameSerializationBinder()
});

如果TypeNameSerializationBinder的BindToName函数中对输出参数assemblyName指定了“程序集”,输出的json就是第二段的样子,而在BindToType时的assemblyName的参数值就会时“程序集”字样,依靠这些信息我们就能使用反射来正确的生成子类对象

1.发送JSON数据到服务器

  • 方案一 : 把JSON格式的字符串序列化成JSON的二进制

#pragma 方案一 : 把JSON格式的字符串序列化成JSON的二进制
- (void)POSTJSON_01
{
    NSString *jsonStr = @"{\"name\":\"大发明家\"}";

    // 把JSON格式的字符串序列化成JSON的二进制
    NSData *jsonData = [jsonStr dataUsingEncoding:NSUTF8StringEncoding];
    [self postJsonWith:jsonData];
}
  • 方案二 : 把字典序列化成JSON格式的二进制

#pragma 方案二 : 把字典序列化成JSON格式的二进制
- (void)POSTJSON_02
{
    NSDictionary *dict = [NSDictionary dictionaryWithObject:@"亚索" forKey:@"name"];

    // 把字典序列化成JSON格式的二进制
    NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dict options:0 error:NULL];
    [self postJsonWith:jsonData];
}
  • 方案三 : 把数组序列化成JSON格式的二进制

#pragma 方案三 : 把数组序列化成JSON格式的二进制
- (void)POSTJSON_03
{
    NSDictionary *dict1 = [NSDictionary dictionaryWithObject:@"牛头" forKey:@"name"];
    NSDictionary *dict2 = [NSDictionary dictionaryWithObject:@"石头人" forKey:@"name"];
    NSArray *arr = @[dict1,dict2];

    // 把数组序列化成JSON格式的二进制
    NSData *jsonData = [NSJSONSerialization dataWithJSONObject:arr options:0 error:NULL];
    [self postJsonWith:jsonData];
}

BSON

看文档Json.net还支持直接反序列化二进制的json文件,缩小文件体积,加快速度,具体使用下回再补了

2.发送json数据到服务器的主方法,传入json数据的二进制

#pragma 发送json数据到服务器的主方法,传入json数据的二进制
- (void)postJsonWith:(NSData *)jsonData
{
    // URL
    NSURL *URL = [NSURL URLWithString:@"http://localhost/php/upload/postjson.php"];
    // 请求
    NSMutableURLRequest *requestM = [NSMutableURLRequest requestWithURL:URL];
    // 设置请求方法
    requestM.HTTPMethod = @"POST";
    // 设置请求体
    requestM.HTTPBody = jsonData;

    // 发送请求
    [[[NSURLSession sharedSession] dataTaskWithRequest:requestM completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        // 处理响应
        if (error == nil && data != nil) {

            // 反序列化
            NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
            NSLog(@"%@",str);

        } else {
            NSLog(@"%@",error);
        }
    }] resume];
}

改进

使用下来后,我觉得在json反序列化时可以增加根据实例对象进行反序列化,先new出具体对象,再反序列化,这样反序列化时就可以明确有哪些成员。

NewtonJson算是比较常用的json解析库了,我的项目中基本都使用该库,因为Unity上也有其相关的库,所以…

(四)序列化自定义对象

1.准备Person类

  • Person类.h文件

@interface Person : NSObject

/// 姓名
@property (nonatomic,copy) NSString *name;

@end
  • Person类.m文件

@implementation Person {
    /// 年龄
    NSString *_age;
}

@end
  • 方案四 : 自定义对象序列化成JSON格式的二进制

#pragma 方案四 : 自定义对象序列化成JSON格式的二进制
- (void)POSTJSON_04
{
    Person *p = [[Person alloc] init];

    // 给对象属性或者私有成员变量赋值
    p.name = @"张小厨zxc";
    [p setValue:@"18" forKey:@"_age"];

    // 先把对象转成字典
    NSDictionary *dict = [p dictionaryWithValuesForKeys:@[@"name",@"_age"]];
    // 再把字典序列化成JSON格式的二进制
    NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dict options:0 error:NULL];
    [self postJsonWith:jsonData];
}

2.发送json数据到服务器的主方法,传入json数据的二进制

#pragma 发送json数据到服务器的主方法,传入json数据的二进制
- (void)postJsonWith:(NSData *)jsonData
{
    // URL
    NSURL *URL = [NSURL URLWithString:@"http://localhost/php/upload/postjson.php"];
    // 请求
    NSMutableURLRequest *requestM = [NSMutableURLRequest requestWithURL:URL];
    // 设置请求方法
    requestM.HTTPMethod = @"POST";
    // 设置请求体
    requestM.HTTPBody = jsonData;

    // 发送请求
    [[[NSURLSession sharedSession] dataTaskWithRequest:requestM completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        // 处理响应
        if (error == nil && data != nil) {

            // 反序列化
            NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
            NSLog(@"%@",str);

        } else {
            NSLog(@"%@",error);
        }
    }] resume];
}

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图