ExpressionTree achieve JSON parser ExpressionTree achieve JSON parser

ExpressionTree achieve JSON parser

  Unlike previous years, this year's Spring Festival, for everyone is unforgettable. He broke it to a new type of coronavirus so that we lived a life "dream" of: eat and sleep, sleep and eat, not the company spent to work, and now such a life in front of us can be that is not practical, not only stop learning to make people feel at ease. So I get a JSON parser years ago to achieve a little bit, serialization / de-serialization of objects into this part of the main use of the ExpressionTree to achieve, and then wrote this article to introduce the project ( see source code ).

First show how to use:

  Student

json deserialized into Student:

var json = "{\"id\":100,\"Name\":\"张三\",\"Sex\":1,\"Birthday\":\"2000-10-10\"}";
var student = JsonParse.To<Student>(json);  

Student serialized to json:

Copy the code
Student = new new Student var 
            { 
                Id = 111, 
                the Name = "TestName", 
                Sex = Sex.Unkown, 
                the Address = "Haidian District", 
                Birthday = the DateTime.Now 
            }; 
            var = JsonParse.ToJson JSON (Student); 
            // { "Id": 111, " Name": "testName", "Sex": "Unkown", "Birthday": "2020-02-15 17:43:31", "Address": " Haidian District" } 
            var JsonOption new new Option = 
            { 
                WriteEnumValue = to true, // used when the sequence of enumeration value 
                DateTimeFormat = "yyyy-MM-dd " // specified format datetime 
            };
            was json2 = JsonParse.ToJson (student, option);
            //{"Id":111,"Name":"testName","Sex":0,"Birthday":"2020-02-15","Address":"北京市海淀区"}
Copy the code

deserialization json List, Ienumerable, Array:

  var json = "[{\"id\":100,\"Name\":\"张三\",\"Sex\":1,\"Birthday\":\"2000-10-10\"},{\"id\":101,\"Name\":\"李四\",\"Sex\":\"female\",\"Birthday\":null,\"Address\":\"\"}]";
  var list = JsonParse.To<List<Student>>(json);
  var list2 = JsonParse.To<IEnumerable<Student>>(json);
  var arr = JsonParse.To<Student[]>(json);        

List <Stuednt> convert json

Copy the code
var list = new List<Student>
            {
                new Student {Id=123,Name="username1",Sex=Sex.Male,Birthday = new DateTime(1980,1,1) },
                new Student {Id=125,Name="username2",Sex=Sex.Female},
            };
            var json1 = JsonParse.ToJson(list, true); //使用缩进格式,默认是压缩的json
            /*
            [
                {
                    "Id":123,
                    "Name":"username1",
                    "Sex":"Male",
                    "Birthday":"1980-01-01 00:00:00",
                    "Address":null
                },
                {
                    "Id":125,
                    "Name":"username2",
                    "Sex":"Female",
                    "Birthday":null,
                    "Address":null
                }
            ] 
            */
            var option = new JsonOption
            {
                Indented = true,    //缩进格式
                DateTimeFormat = "yyyy-MM-dd",
                IgnoreNullValue = true //忽略null输出
            };
            var json2 = JsonParse.ToJson(list, option);
            /*
               [
                    {
                        "Id":123,
                        "Name":"username1",
                        "Sex":"Male",
                        "Birthday":"1980-01-01"
                    },
                    {
                        "Id":125,
                        "Name":"username2",
                        "Sex":"Female"
                    }
                ]
             */
Copy the code

json into Dictironary:

Json to the Dictionary // 
var json = "{\" confirmed cases \ ": 66580, \" suspected cases \ ": 8969, \" cured cases \ ": 8286, \" deaths \ ": 1524}"; 
var dic JsonParse.To = <the Dictionary <String, int >> (JSON); 
var DIC2 JsonParse.To = <the IDictionary <String, int >> (JSON);

 JsonParse provides some can override the object serialization / de-serialization of the static method, the interior is actually calling JsonSerializer to complete more complex functions also need to use JsonSerializer to achieve, this is not the focus will not go introduced.

  For JSON parsing comprises two main functions: serializing and deserializing serialized object is to convert a string JSON deserialization JSON string is converted to the specified object. Several core objects related to the project have JsonReader, JsonWriter, ITypeConverter, IConverterCreator etc., introduced one by one below.

1, JsonReader json reader

  JsonReader can be simply understood as a scanner json string, in accordance with the scanning json syntax rules, each sweep out a JsonTokenType their corresponding values, JsonTokenType enumeration defines:

  View Code

String scanning method Read ():

  View Code

  As can be seen from Read method maintains a ReadState JsonReader internal state machine, each call is to be resolved in accordance with the next token on a ReadState, so that both the driving method of an internal branch jump, while the easy correction format json test, for example: met  { (StartObject) next valid character (except blank character) is only  " (PropertyName) or  } (EndObject) one, when the ReadState = StartObject should go to perform the ReadProperty () method, and in ReadProperty () method where only requires  and  }  two characters to do the right response, there are other characters illustrate the json file format is not correct, throw an exception on the line, so ReadProperty () method of the core code is as follows:

  View Code

.... jump and the like check formats are similar to other methods of treatment methods.

  a token verification is more troublesome where the container (JSONObject and JsonArray) nested closed after the correct symbol, i.e., { and } , [ and ] must occur in pairs, for example: [{}}] The error json string, if only use a token to validate the next token is legitimate, is unable to determine the json this is not legitimate, then LIFO Stack feature is very suitable for this scenario, and we can help verify the Stack json: first encountered [performs a push operation; {second, push continue;} third, the pull operation of the value of the stack is determined whether the value of the current is closed, the value of the stack {,} in pairs coincides with, the third character is legitimate, the value at this time is the top of the stack [;} the fourth character, the stack operation, the value of the stack is [,} and not in pairs, illegal value, verification ending.

  JsonReader core function is to json text of dismantling and verification, is the core method Read (), call Read () method will be in the presence of 3: 1 returns true, correct to read a document and did not finish JsonTokenType 2. returns false, the right to read a document and JsonTokenType have all been read 3. abnormal, json format is incorrect or does not meet the configuration requirements. Deserialization upper JsonReader are dependent to complete, after reading a json using JsonReader JsonTokenType is obtained and the corresponding values ​​of a group, as a hierarchical relationship between tokentype would be involved by the following ITypeConverter or JsonToken other objects processed.

2, JosnWriter json writer

  JosnWriter JsonReader functions and on the contrary, the data is output according to specifications json json string serialization function classes are ultimately to JosnWriter accomplished. Write every method call JsonWriter writes a JsonTokenType value, of course, they also need to write a check value is legitimate, check the check logic and JsonReader similar function is relatively simple and will not go introduced, interested students you can directly look at the code, the code address at the end of the document.

3, (trans) serial interfaces ITypeConverter

 References between the main class diagram:

  

  ITypeConverter interface is the whole object serialization / core deserialization process, responsibilities ITypeConverter is relying on JsonReader, JsonWriter to achieve a particular type of object (de) serialization, but the light has ITypeConverter not enough, because it is a specific object (Anti ) serializer, a ITypeConverter implementation class can only resolve one or a class of objects, an object will be used to resolve a number of ITypeConverter, for external callers who do not know when to use which ITypeConverter what this work is handed over IConverterCreator factory to complete, look at the definition of IConverterCreator:

  View Code

Using this facility to create a need to call before ITypeConverter CanConvert method to determine a given Type support, when returning true you can go to create the corresponding TypeConverter, or else create out also does not work, so you need to have a bunch of IConverterCreator candidates to look for the caller, and then to traverse these candidates call CanConvert method returns true when traversing to a candidate, you can create ITypeConverter start the work, based on a TypeConverterProvider this abstract class:

  View Code

To be able to extend the use IConverterCreator custom implementation, there is provided a method AddConverterFactory can add custom IConverterCreator from the outside. The default method is to achieve Build traverse AllConverterFactories, and then determine whether to create ITypeConverter, they meet the conditions to call the Create method to create ITypeConverter IConverterCreator return, the entire plant builder to achieve closure, in theory, as long as AllConverterFactories inside IConverterCreator enough or strong enough, It can convert all types of type, then this factory builder can use IConverterCreator create ITypeConverter to achieve any kind of (anti) serialization work.

4, with ExpressionTree of several implementations of ITypeConverter  

 4.1 TypeConverterBase

  The use of the expression tree generator function delegate, delegate and then cached, and code execution performance can be written fairly static. TypeConverterBase extract a public property Func <object> CreateInstance, the purpose is to create a Type deserialized object is to call for the commission is compiled using expression trees:

  View Code

This method first determine whether the type constructor no-argument, if there is direct through Expression.New (type) to construct, if not to find at least a constructor parameter to construct, when tectonic zone argument to the constructor is required these parameters are passed, the default implementation is directly transmitted current default value of the parameter type, of course, may be specified by the configuration parameter data values ​​or the like. Gets a type of default value expressions Expression.Default (type), if the type is int, is equivalent to default (int), if the type is string, is equivalent to default (string), and so on. Then constant expression Expression.Constant (defaultValue) is converted to Expression, the result added to the converted List <Expression>, and then using the constructor overloads expression newExp = Expression.New (constructor, parametExps), converted into lambad expression Expression.Lambda <Func <object >> (newExp), you can call the Compile method to generate commissioned.

  With Func <object> CreateInstance this delegate method, instantiate an object need only be executed commissioned on the line, you do not have to create an object of reflection.

  TypeConverterBase the implementation class is generally classified into three types, type parser processing JsonObject: ObjectConverter, DictionaryConverter, processing JsonArray parser type: EnumberableConverter (concrete realization ListConverter, ArrayConverter ...); Json process value types (JsonString, JsonNumber , JsonBoolean, JsonNull) parser: ValueConverter. Each resolver is done for the respective characteristics of type JSON (de) serialization.

 4.2 Object Parser ObjectConverter

  In order to make the object property / field and can be performed in JsonObject Property interconversion, we define two delegate property: Func <object, object> GetValue, set the property / field value Action <object, object> SetValue. The parameters are defined using the object type, object is to ensure the versatility of the method. GetValue is the get property / method delegate field value, the first reference object is the current instance of the object class, the object is the return value of the property / field corresponds. GetValue look commissioned the generated code:

  View Code

First, the definition of a good process parameters var instanceExp = Expression.Parameter (typeof (object), "instance"), the reference is of type object, when using a real need to be converted into its type, using Expression.Convert (instanceExp, MemberInfo .DeclaringType), Expression.Convert do is type conversion (Expression.TypeAs can also type conversion, but the conversion if the type is a value type will complain, can only be used to convert a reference type), then use Expression.PropertyOrField (instanceTypeExp, MemberInfo.Name), passing the name of a member instance and can obtain the value of the member, this logic is equivalent to the GetValue method of the following pseudo-code:

1
2
3
4
5
6
protected  object  GetValue( object  obj)
         {
             var  instance = (目标类型)obj;
             var  value = instance.目标属性/字段;
             return  ( object )value;
         }

Look at SetValue generation logic commissioned:

  View Code

Assignment need not have a return value, the first parameter is an instance of the object, the second parameter is a member of the object, through Expression.Parameter method declaration, Expression.PropertyOrField is to obtain an expression property / field corresponds to the instance of the static code. such wording property / field names, members assignment expression: Expression.Assign (memberExp, Expression.Convert (valueExp, MemberType)), a member of the ginseng statement that object, the same need to call Expression.Convert (valueExp, MemberType) to convert into a real type. Then use Expression.Lambda the Compile methods can generate goals commissioned.

  A class where there will be multiple properties / fields, each property / field corresponding to their needs GetValue / SetValue, we will generate GetValue / SetValue commissioned unified placed MemberDefinition class, a MemberDefinition only manage a membership information ( PropertyInfo or FieldInfo) read and write commissioned generation, then in ObjectConverter which maintains a list of MemberDefinition public IEnumerable <MemberDefinition> MemberDefinitions current class to map multiple properties / fields, each assigned to a member or writing values, just need to find corresponding MemberDefinition, then call its GetValue / SetValue commissioned it.

 4.3 Dictionary type parser DictionaryConverter

To handle DictionaryConverter Dictionary <,> and mutual conversion between JsonObject as a generic interface, with the type of the key value needed to hold the two properties

1
2
3
public  Type KeyType {  get protected  set ; }
 
public  Type ValueType {  get protected  set ; }

 Both Type attribute type to use when converting the type of assignment / write values. Method and object members is not the same assignment, read the dictionary may be accomplished by a key index, a delegate assignment dictionary: Action <object, object, object>, the first parameter is a dictionary example, the second argument is the key value, the third value is the value of the parameter, performing this call is equal to delegate this code: dic [key] = value; look delegate this expression generated code:

  View Code

The delegate has no return value into three types of object parameters are defined by Expression.Parameter, respectively, and then converted into the respective real data type, then an index to find the corresponding reflector PropertyInfo: type.GetProperty ( "Item", new type [] {KeyType}) (index attribute named Item default), to give the indexer Expression.MakeIndex (dicExp, property, new Expression [] {keyExp}), a value corresponding to the read key phrase, the index is assignment, then you must also use Expression.Assign (indexExp, valueExp) to complete, so the index is assigned by the commission to get. The dictionary key value acquired values ​​delegate: Func <object, object, object> and the logical assignment is basically the same, just to get the indexer returns the result of the bin, the code is not posted.

4.4 may be iteratively type (IEnumerable interface implementation type) resolver EnumerableConverter

   Achieve conversions between the interface and the type IEnumerable JsonArray main use of the delegate two functions: Func <object, IEnumerator> GetEnumerator and Action <object, object> AddItem, respectively correspond to reading and writing, reading is to get IEnumerable iterator GetEnumerator (), then traverse iterator; write is to add elements to the collection, and ultimately is a collection calls itself "Add" method, since not all data collection methods to add name called Add, so EnumerableConverter is an abstract class, only achieved a common logic part, embodied by the specific implementation class to complete (for example: ListConverter, ArrayConverter ...). Paste generated code acquires the set iterator delegate delegate generated code data is added:

  BuildGetEnumberatorMethod
  BuildAddItemMethod

   Just call when using EnumerableConverter serialized object GetEnumerator delegate, get iterators IEnumerator, the iterator will traverse the output of each item to json on it. AddItem performed delegate deserialize objects set equal to invoke their methods to add data to complete filling of the collection of data. But the array are immutable, there is no method to add an element of how to handle it? The approach here is to construct an array of accomplished first by List, you can add data using List.Add method, in the end unified call List of ToArray () method is converted into the target array. So ArrayConverter is inherited from ListConverter, and rewrite the deserialization method of the parent class ListConverter, call the list ToArray method in the parent class has been dealt with is complete.

  There are a lot of specific implementation here is not to introduce, mainly the expression tree realize this stuff written as study notes, incidentally share.

  Write this project is mainly to learn and use json analytic expression tree, in which part of the design ideas Newtonsoft.Json reference source, subject to my level, plus the project did not fully tested, there must be a lot of problems welcome to correct me bigwigs put forward, with the hope that we learn together and progress. Lastly, I hope an early end to the epidemic, to go back early to move bricks.

  Paste Source Address: https://github.com/zhangmingjian/RapidityJson

Guess you like

Origin www.cnblogs.com/Leo_wl/p/12518993.html