504 lines
24 KiB
C#
504 lines
24 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq.Expressions;
|
||
using System.Reflection;
|
||
|
||
namespace CPF.Windows.Json.Deserialize
|
||
{
|
||
[ExpressionBuildType(DeserializeBuildTypeEnum.KeyValueObject)]
|
||
internal class KeyValueObjectBuild : ExpressionJsonResolve
|
||
{
|
||
internal static BlockExpression Build(Type type)
|
||
{
|
||
List<Expression> methodCall = new List<Expression>();
|
||
LabelTarget returnValueLable = Expression.Label(type, "returnValue");
|
||
|
||
if (type.IsValueType)
|
||
{
|
||
/*
|
||
ReadObjLeft()
|
||
*/
|
||
methodCall.Add(Expression.Call(ExpressionMembers.Reader, JsonReader._ReadObjLeft));
|
||
}
|
||
else
|
||
{
|
||
/*
|
||
if(_ReadNullOrObjLeft)
|
||
return null;/return default(ValueType)
|
||
*/
|
||
Expression ifReadNullOrObjLeftReturnNull1 = Expression.IfThen(Expression.Call(ExpressionMembers.Reader, JsonReader._ReadNullOrObjLeft),
|
||
Expression.Return(returnValueLable, Expression.Constant(null, type)
|
||
));
|
||
methodCall.Add(ifReadNullOrObjLeftReturnNull1);
|
||
}
|
||
|
||
/*
|
||
Model m=new Model();
|
||
*/
|
||
NewExpression newCtor = null;
|
||
if (type.IsValueType)
|
||
{
|
||
List<Expression> ctorArgs = null;
|
||
var ctor = type.GetValueTypeCtor(ref ctorArgs);
|
||
if (ctor == null)
|
||
newCtor = Expression.New(type);
|
||
else
|
||
newCtor = Expression.New(ctor, ctorArgs);
|
||
}
|
||
else
|
||
{
|
||
if (type.IsInterface)
|
||
{
|
||
//newCtor = Expression.New((Type)typeof(InterfaceImplementation<>).MakeGenericType(type).GetField("Proxy").GetValue(null));
|
||
throw new NotSupportedException();
|
||
}
|
||
else
|
||
{
|
||
List<Expression> ctorArgs = null;
|
||
var ctor = type.GetClassCtor(ref ctorArgs);
|
||
newCtor = Expression.New(ctor, ctorArgs);
|
||
}
|
||
}
|
||
ParameterExpression newModel = Expression.Variable(type, "newModel");
|
||
methodCall.Add(Expression.Assign(newModel, newCtor));
|
||
|
||
/*
|
||
if(reader.ReadBoolObjRight)
|
||
return m;
|
||
*/
|
||
methodCall.Add(Expression.IfThen(Expression.Call(ExpressionMembers.Reader, JsonReader._ReadBoolObjRight), Expression.Return(returnValueLable, newModel)));
|
||
|
||
/*
|
||
int moveNext=2;
|
||
*/
|
||
methodCall.Add(ExpressionMembers.MoveNextAssignOne); //Add moveNext localVirbale
|
||
|
||
/*
|
||
if(jsonDeserializeOption.IsFirstUpper)
|
||
while(moveNext-->0)
|
||
{ }
|
||
else if(jsonDeserializeOption.IsFirstLower)
|
||
while(moveNext-->0)
|
||
{ }
|
||
else
|
||
while(moveNext-->0)
|
||
{ }
|
||
*/
|
||
var charTries = type.GetCharTries();
|
||
if (charTries.Childrens.Count > 0)
|
||
methodCall.Add(GenerateWhile(newModel, type, charTries));
|
||
|
||
/*
|
||
reader.ReadObjRight()
|
||
*/
|
||
methodCall.Add(Expression.Call(ExpressionMembers.Reader, JsonReader._ReadObjRight));
|
||
|
||
/*
|
||
return newModel;
|
||
*/
|
||
methodCall.Add(Expression.Return(returnValueLable, newModel));
|
||
methodCall.Add(Expression.Label(returnValueLable, newModel));
|
||
|
||
return Expression.Block(new[] { ExpressionMembers.AfterFormatKey, ExpressionMembers.CurrentIdx, ExpressionMembers.IsValueFormat, ExpressionMembers.ValueLength, ExpressionMembers.FormatResult, ExpressionMembers.CharVariable, ExpressionMembers.MoveNext, ExpressionMembers.IsArrive, newModel }, methodCall);
|
||
}
|
||
|
||
private static BlockExpression GenerateWhile(ParameterExpression newModel, Type type, CharTries charTries)
|
||
{
|
||
//(state):0==The initial state does not change,1==Capital letters,2==Initial lowercase
|
||
/*
|
||
char c;
|
||
while(moveNext-->0)
|
||
{
|
||
}
|
||
*/
|
||
ParameterExpression firstChar = Expression.Variable(typeof(char), "firstChar");
|
||
LabelTarget tailOfMethod = Expression.Label(typeof(void), "tailOfMethod");
|
||
LabelTarget whileBreak = Expression.Label();
|
||
LoopExpression loopExpression = Expression.Loop(Expression.IfThenElse(ExpressionMembers.MoveNextDecrement,
|
||
ReturnFunc<Expression>(() =>
|
||
{
|
||
Expression[] expressions = new Expression[6];
|
||
/*
|
||
isArrive=false;
|
||
*/
|
||
expressions[0] = ExpressionMembers.IsArriveAssignFalse;
|
||
|
||
/*
|
||
2019-11-10 16:55:31,Add two configuration options of IgnoreJsonKeys and IsIgnoreExtraKeysInJSON
|
||
*/
|
||
expressions[1] = GenerateIgnoreKeys(type, tailOfMethod);
|
||
|
||
/*
|
||
* if(handler.option.GlobalKeyFormat!=null)
|
||
GenerateGlobalKeyFormat()
|
||
else
|
||
ReadKey()
|
||
*/
|
||
expressions[2] = Expression.IfThenElse(
|
||
Expression.NotEqual(Expression.MakeMemberAccess(ExpressionMembers.JsonDeserializeOption, JsonDeserializeOption._GlobalKeyFormat), Expression.Constant(null, JsonDeserializeOption._GlobalKeyFormat.FieldType)),
|
||
GenerateGlobalKeyFormat(type, newModel, tailOfMethod),
|
||
GenerateKeyValueObjectReadKey(firstChar, charTries, newModel)
|
||
);
|
||
|
||
/*
|
||
if(!isArrive)
|
||
throw
|
||
*/
|
||
expressions[3] = Expression.IfThen(Expression.Equal(ExpressionMembers.IsArrive, Expression.Constant(false, typeof(bool))), Expression.Throw(Expression.New(JsonDeserializationTypeResolutionException._JsonDeserializationTypeResolutionExceptionCtor, ExpressionMembers.Reader, type.IsInterface ? (Expression)Expression.Constant(type, typeof(Type)) : Expression.Call(newModel, type.GetMethod("GetType")))));
|
||
|
||
/*
|
||
Label -> tailOfMethod:
|
||
*/
|
||
expressions[4] = Expression.Label(tailOfMethod);
|
||
|
||
/*
|
||
if(reader.ReadComma()==true)
|
||
moveNext++;
|
||
*/
|
||
expressions[5] = ExpressionMembers.IfReadBoolCommaIsTrueSoMoveNextIncrement;
|
||
|
||
return Expression.Block(expressions);
|
||
})
|
||
, Expression.Break(whileBreak)));
|
||
return Expression.Block(new[] { firstChar }, loopExpression, Expression.Label(whileBreak));
|
||
}
|
||
|
||
private static Expression GenerateIgnoreKeys(Type type, LabelTarget tailOfMethod)
|
||
{
|
||
#region
|
||
/*
|
||
if(jsonDeserializeHandler.Option.JsonCharacterReadState == JsonCharacterReadStateEnum.None && jsonDeserializeHandler.Option.GlobalKeyFormat == null)
|
||
{
|
||
string key = reader.ReadString();
|
||
|
||
if(IsIgnoreExtraKeysInJSON){
|
||
if(TypeKeysCache<T>.NotContains(key))
|
||
{
|
||
reader.ReadColon();
|
||
Reader.ReadObject();
|
||
goto tailOfMethod;
|
||
}
|
||
|
||
if(IgnoreJsonKeysHasValue)
|
||
{
|
||
if(IgnoreKey.Contains(key))
|
||
{
|
||
reader.ReadColon();
|
||
Reader.ReadObject();
|
||
goto tailOfMethod;
|
||
}
|
||
}
|
||
|
||
if(key!=null)
|
||
Rollback(key.Length+2);
|
||
}
|
||
*/
|
||
#endregion
|
||
ParameterExpression key = Expression.Variable(typeof(string), "key");
|
||
return Expression.IfThen(Expression.AndAlso(Expression.Equal(ExpressionMembers.JsonCharacterReadState, Expression.Constant(JsonCharacterReadStateEnum.None, typeof(JsonCharacterReadStateEnum))), ExpressionMembers.GlobalKeyFormatEqualNull),
|
||
Expression.Block(new[] { key },
|
||
//string key=reader.ReadString();
|
||
Expression.Assign(key, Expression.Call(ExpressionMembers.Reader, JsonReader._ReadString)),
|
||
//if(IsIgnoreExtraKeysInJSON)
|
||
IfIsIgnoreExtraKeysInJsonThenSkipObject(key, type, tailOfMethod),
|
||
//if(IgnoreJsonKeysHasValue)
|
||
IfIgnoreJsonKeysHasValueThenSkipObject(key, tailOfMethod),
|
||
//if(key!=null)
|
||
Expression.IfThen(Expression.NotEqual(key, Expression.Constant(null, typeof(string))),
|
||
//Rollback(key.Length+2);
|
||
Expression.Call(ExpressionMembers.Reader, JsonReader._Rollback, Expression.Add(Expression.Property(key, typeof(string).GetProperty("Length")), Expression.Constant(2, typeof(int))))
|
||
)
|
||
)
|
||
);
|
||
}
|
||
|
||
private static Expression IfIsIgnoreExtraKeysInJsonThenSkipObject(ParameterExpression key, Type type, LabelTarget tailOfMethod)
|
||
{
|
||
return Expression.IfThen(Expression.Equal(Expression.MakeMemberAccess(ExpressionMembers.JsonDeserializeOption, JsonDeserializeOption._IsIgnoreExtraKeysInJSON), Expression.Constant(true, typeof(bool))),
|
||
//if(TypeKeysCache<T>.NotContains(key))
|
||
Expression.IfThen(Expression.Equal(Expression.Call(TypeUtils.GetContainsMethodInfo(type), key), Expression.Constant(true, typeof(bool))),
|
||
/*
|
||
reader.ReadColon();
|
||
Reader.SkipObject();
|
||
goto tailOfMethod;
|
||
*/
|
||
ReadColonAndSkipObjectAndGotoTailOfMethod(tailOfMethod)
|
||
)
|
||
);
|
||
}
|
||
|
||
private static Expression IfIgnoreJsonKeysHasValueThenSkipObject(ParameterExpression key, LabelTarget tailOfMethod)
|
||
{
|
||
return Expression.IfThen(Expression.Equal(Expression.MakeMemberAccess(ExpressionMembers.JsonDeserializeOption, JsonDeserializeOption._IgnoreJsonKeysHasValue), Expression.Constant(true, typeof(bool))),
|
||
//if(IgnoreKey.Contains(key))
|
||
Expression.IfThen(Expression.Call(Expression.MakeMemberAccess(ExpressionMembers.JsonDeserializeOption, JsonDeserializeOption._IgnoreJsonKeys), JsonDeserializeOption._IgnoreJsonKeyContains, key),
|
||
/*
|
||
reader.ReadColon();
|
||
Reader.SkipObject();
|
||
goto tailOfMethod;
|
||
*/
|
||
ReadColonAndSkipObjectAndGotoTailOfMethod(tailOfMethod)
|
||
)
|
||
);
|
||
}
|
||
|
||
private static Expression ReadColonAndSkipObjectAndGotoTailOfMethod(LabelTarget tailOfMethod)
|
||
{
|
||
return Expression.Block(
|
||
Expression.Call(ExpressionMembers.Reader, JsonReader._ReadColon),
|
||
Expression.Call(ExpressionMembers.Reader, JsonReader._SkipObj, ExpressionMembers.JsonDeserializeHandler),
|
||
Expression.Goto(tailOfMethod)
|
||
);
|
||
}
|
||
|
||
|
||
|
||
private static Expression GenerateGlobalKeyFormat(Type type, ParameterExpression newModel, LabelTarget tailOfMethod)
|
||
{
|
||
return Expression.Block(
|
||
|
||
/*
|
||
var afterFormatKey = handler.option.GlobalKeyFormat.Invoke(reader.ReadString(),type)
|
||
*/
|
||
Expression.Assign(ExpressionMembers.AfterFormatKey,
|
||
Expression.Call(ExpressionMembers.GlobalKeyFormat, JsonDeserializeOption._GlobalKeyFormatInvoke,
|
||
Expression.Call(ExpressionMembers.Reader, JsonReader._ReadString), Expression.Constant(type, typeof(Type)))),
|
||
|
||
/*
|
||
if(IsIgnoreExtraKeysInJSON){
|
||
if(!modelKeys.Contains(afterFormatKey))
|
||
{
|
||
reader.ReadColon();
|
||
Reader.ReadObject();
|
||
goto zuihou;
|
||
}
|
||
if(IsOpenIgnoreKey)
|
||
if(IgnoreKey.Contains(key))
|
||
{
|
||
reader.ReadColon();
|
||
Reader.ReadObject();
|
||
goto zuihou;
|
||
}
|
||
*/
|
||
IfIsIgnoreExtraKeysInJsonThenSkipObject(ExpressionMembers.AfterFormatKey, type, tailOfMethod),
|
||
IfIgnoreJsonKeysHasValueThenSkipObject(ExpressionMembers.AfterFormatKey, tailOfMethod),
|
||
/*
|
||
reader.ReadColon()
|
||
*/
|
||
Expression.Call(ExpressionMembers.Reader, JsonReader._ReadColon),
|
||
/*
|
||
Switch(afterFormatKey)
|
||
case 'Name':
|
||
ReadValue()....
|
||
*/
|
||
Expression.Switch(
|
||
typeof(void),
|
||
ExpressionMembers.AfterFormatKey,
|
||
null, null,
|
||
ReturnFunc(() =>
|
||
{
|
||
var members = type.GetModelMembers();
|
||
SwitchCase[] switchCases = new SwitchCase[members.Count];
|
||
for (int i = 0; i < members.Count; i++)
|
||
{
|
||
var item = members[i];
|
||
switchCases[i] = Expression.SwitchCase(GenerateKeyValueObjectReadValue(item.Value, newModel), Expression.Constant(item.Key));
|
||
|
||
}
|
||
return switchCases;
|
||
})
|
||
),
|
||
/*
|
||
isArrive=true;
|
||
*/
|
||
ExpressionMembers.IsArriveAssignTrue
|
||
);
|
||
}
|
||
|
||
private static Expression GenerateKeyValueObjectReadValue(MemberExtension member, ParameterExpression newModel)
|
||
{
|
||
if (!member.IsProperty || (member.IsProperty && member.PropertyInfo.CanWrite))
|
||
{
|
||
var valueFormatAttribute = member.MemberInfo.GetCustomAttribute<ValueFormatAttribute>() ?? member.MemberInfo.DeclaringType.GetCustomAttribute<ValueFormatAttribute>();
|
||
if (valueFormatAttribute != null)//part
|
||
{
|
||
return (GenerateValueFormatCode(Expression.Constant(valueFormatAttribute, typeof(ValueFormatAttribute)), ValueFormatAttribute._ReadValueFormat, new[] { ExpressionMembers.JsonRemoveQuoteAndSubstring, Expression.Constant(member.Type, typeof(Type)), ExpressionMembers.JsonDeserializeHandler, ExpressionMembers.IsValueFormat }, newModel, member));
|
||
}
|
||
else //global
|
||
{
|
||
/*
|
||
if( jsonDeserializeHandler.jsonDeserializeOption .globalValueFormat!=null)
|
||
{
|
||
}
|
||
else
|
||
model.N=ReadJson();
|
||
*/
|
||
return (Expression.IfThenElse(ExpressionMembers.GlobalValueFormatNotEqualNull, GenerateValueFormatCode(ExpressionMembers.GlobalValueFormat, JsonDeserializeOption._GlobalValueFormatInvoke, new[] { ExpressionMembers.JsonRemoveQuoteAndSubstring, Expression.Constant(member.Type, typeof(Type)), ExpressionMembers.JsonDeserializeHandler, ExpressionMembers.IsValueFormat }, newModel, member), Expression.Assign(Expression.MakeMemberAccess(newModel, member.MemberInfo), ExpressionMembers.GetMethodCall(member.Type))));
|
||
}
|
||
}
|
||
else
|
||
/* SkipObj(); */
|
||
return (Expression.Call(ExpressionMembers.Reader, JsonReader._SkipObj, ExpressionMembers.JsonDeserializeHandler));
|
||
}
|
||
|
||
private static BlockExpression GenerateValueFormatCode(Expression formatDeclareInstance, MethodInfo callFormat, Expression[] paras, ParameterExpression newModel, MemberExtension member)
|
||
{
|
||
Expression[] expressions = new Expression[6];
|
||
/*
|
||
reader.BeforAnnotation();
|
||
reader.RollBackChar()
|
||
*/
|
||
expressions[0] = Expression.Call(ExpressionMembers.Reader, JsonReader._BeforAnnotation);
|
||
expressions[1] = Expression.Call(ExpressionMembers.Reader, JsonReader._RollbackChar);
|
||
/*
|
||
currentIdx= Length - reader.Remaining
|
||
*/
|
||
expressions[2] = ExpressionMembers.CurrentIdxAssignReming;
|
||
/*
|
||
valueLength=reader.Skipobj()
|
||
*/
|
||
expressions[3] = (ExpressionMembers.ValueLengthAssignSkipObj);
|
||
/*
|
||
object formatResult = ValueFormat.ReadValueFormat( reader.Substring(currentIdx,valueLength) ,out isValueFormat);
|
||
*/
|
||
expressions[4] = (Expression.Assign(ExpressionMembers.FormatResult, Expression.Call(formatDeclareInstance, callFormat, paras)));
|
||
/*
|
||
if(isValueFormat==true)
|
||
model.N=(Convert)obj;
|
||
else
|
||
reader.Rollback(valueLength)
|
||
m.N=ReadJson();//string
|
||
*/
|
||
expressions[5] = (Expression.IfThenElse(ExpressionMembers.IsValueFormatEqualTrue, Expression.Assign(Expression.MakeMemberAccess(newModel, member.MemberInfo), Expression.Convert(ExpressionMembers.FormatResult, member.Type)), Expression.Block(Expression.Call(ExpressionMembers.Reader, JsonReader._Rollback, ExpressionMembers.ValueLength), Expression.Assign(Expression.MakeMemberAccess(newModel, member.MemberInfo), ExpressionMembers.GetMethodCall(member.Type)))));
|
||
|
||
return Expression.Block(expressions);
|
||
}
|
||
|
||
private static Expression GenerateKeyValueObjectReadKey(ParameterExpression firstChar, CharTries charTries, ParameterExpression newModel)
|
||
{
|
||
|
||
return
|
||
Expression.IfThenElse(
|
||
Expression.NotEqual(ExpressionMembers.JsonCharacterReadState, Expression.Constant(JsonCharacterReadStateEnum.IgnoreCase, typeof(JsonCharacterReadStateEnum))),
|
||
GenerateKeyValueObjectReadKeyWithInitial(firstChar, charTries, newModel),
|
||
GenerateKeyValueObjectReadKeyWithIgnoreCase(charTries, newModel));
|
||
}
|
||
|
||
private static Expression GenerateKeyValueObjectReadKeyWithInitial(ParameterExpression firstChar, CharTries charTries, ParameterExpression newModel)
|
||
{
|
||
return Expression.Block(
|
||
/*
|
||
_ReadQuotes();
|
||
*/
|
||
Expression.Call(ExpressionMembers.Reader, JsonReader._ReadQuotes),
|
||
/*
|
||
switch (handler.Option.InitialReadState)
|
||
{
|
||
case InitialReadStateEnum.None:
|
||
firstChar = reader.GetChar();
|
||
break;
|
||
case InitialReadStateEnum.Upper:
|
||
firstChar = char.ToUpper(reader.GetChar());
|
||
break;
|
||
case InitialReadStateEnum.Lower:
|
||
firstChar = char.ToLower(reader.GetChar());
|
||
break;
|
||
}
|
||
*/
|
||
|
||
Expression.Switch(typeof(void), ExpressionMembers.JsonCharacterReadState,
|
||
null, null,
|
||
Expression.SwitchCase(Expression.Assign(firstChar, ExpressionMembers.GetChar), Expression.Constant(JsonCharacterReadStateEnum.None, typeof(JsonCharacterReadStateEnum))),
|
||
Expression.SwitchCase(Expression.Assign(firstChar, Expression.Call(typeof(char).GetMethod("ToUpper", new[] { typeof(char) }), ExpressionMembers.GetChar)), Expression.Constant(JsonCharacterReadStateEnum.InitialUpper, typeof(JsonCharacterReadStateEnum))),
|
||
Expression.SwitchCase(Expression.Assign(firstChar, Expression.Call(typeof(char).GetMethod("ToLower", new[] { typeof(char) }), ExpressionMembers.GetChar)), Expression.Constant(JsonCharacterReadStateEnum.InitialLower, typeof(JsonCharacterReadStateEnum)))
|
||
),
|
||
/*
|
||
Switch(firstChar)
|
||
case 'N'
|
||
Switch(getChar())
|
||
case 'a'
|
||
Switch(getChar())
|
||
case 'm'
|
||
Switch(getChar())
|
||
case 'e'
|
||
Switch(getChar())
|
||
case '"':
|
||
ReadValue()...
|
||
*/
|
||
GenerateSwitchCodeByChar(firstChar, charTries, newModel)
|
||
);
|
||
|
||
}
|
||
|
||
private static Expression GenerateKeyValueObjectReadKeyWithIgnoreCase(CharTries charTries, ParameterExpression newModel)
|
||
{
|
||
return Expression.Block(
|
||
/*
|
||
_ReadQuotes();
|
||
*/
|
||
Expression.Call(ExpressionMembers.Reader, JsonReader._ReadQuotes),
|
||
|
||
/*
|
||
Switch(getChar())
|
||
case 'N'
|
||
Switch(getChar())
|
||
case 'a'
|
||
Switch(getChar())
|
||
case 'm'
|
||
Switch(getChar())
|
||
case 'e'
|
||
Switch(getChar())
|
||
case '"':
|
||
ReadValue()...
|
||
*/
|
||
GenerateSwitchCodeByChar(null, charTries, newModel, true)
|
||
);
|
||
|
||
}
|
||
|
||
private static SwitchExpression GenerateSwitchCodeByChar(ParameterExpression firstChar, CharTries charTries, ParameterExpression newModel, bool isToLower = false)
|
||
{
|
||
List<SwitchCase> switchCases = new List<SwitchCase>();
|
||
|
||
if (charTries.IsValue)
|
||
{
|
||
//case '"'
|
||
var caseQuotes = Expression.SwitchCase(
|
||
|
||
Expression.Block(
|
||
typeof(void),
|
||
/*
|
||
ReadColon();
|
||
*/
|
||
Expression.Call(ExpressionMembers.Reader, JsonReader._ReadColon),
|
||
GenerateKeyValueObjectReadValue(charTries.Member, newModel),
|
||
/*
|
||
isArrive=true;
|
||
*/
|
||
ExpressionMembers.IsArriveAssignTrue
|
||
)
|
||
, Expression.Constant('"', typeof(char)));
|
||
|
||
switchCases.Add(caseQuotes);
|
||
}
|
||
|
||
if (charTries.Childrens.Count > 0)
|
||
{
|
||
foreach (var item in charTries.Childrens)
|
||
{
|
||
char c = item.Val;
|
||
SwitchCase caseOrdinary = Expression.SwitchCase( //When there are two duplicate case items, the expression takes the first one automatically
|
||
GenerateSwitchCodeByChar(null, item, newModel, isToLower),
|
||
Expression.Constant(isToLower ? char.ToLower(c) : c, typeof(char)));
|
||
|
||
switchCases.Add(caseOrdinary);
|
||
}
|
||
}
|
||
|
||
return Expression.Switch(isToLower ? Expression.Call(typeof(char).GetMethod("ToLower", new[] { typeof(char) }), ExpressionMembers.GetChar) : firstChar ?? ExpressionMembers.GetChar, switchCases.ToArray());
|
||
}
|
||
|
||
}
|
||
|
||
}
|