504 lines
24 KiB
C#
Raw Normal View History

2023-11-21 23:05:03 +08:00
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 change1==Capital letters2==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());
}
}
}