33using System . Linq ;
44using System . Linq . Expressions ;
55using System . Reflection ;
6+ using Microsoft . CodeAnalysis ;
67using Microsoft . CodeAnalysis . CSharp ;
78using Microsoft . CodeAnalysis . CSharp . Syntax ;
9+ using static Microsoft . CodeAnalysis . CSharp . SyntaxFactory ;
810
911namespace CSharpScriptSerialization
1012{
1113 public class PropertyCSScriptSerializer < T > : ConstructorCSScriptSerializer < T >
1214 {
1315 private readonly IReadOnlyCollection < PropertyData > _propertyData ;
16+ private readonly IReadOnlyCollection < PropertyData > _hiddenPropertyData ;
1417
1518 public PropertyCSScriptSerializer ( )
1619 : this ( ( Func < T , object > [ ] ) null )
@@ -36,63 +39,154 @@ public PropertyCSScriptSerializer(IReadOnlyDictionary<string, Func<T, object, bo
3639 public PropertyCSScriptSerializer ( IReadOnlyDictionary < string , Func < T , object , bool > > propertyConditions ,
3740 IReadOnlyCollection < Func < T , object > > constructorParameterGetters ,
3841 IReadOnlyDictionary < string , Func < T , object > > propertyValueGetters )
42+ : this ( propertyConditions , constructorParameterGetters , propertyValueGetters , null , null )
43+ {
44+ }
45+
46+ public PropertyCSScriptSerializer ( IReadOnlyDictionary < string , Func < T , object , bool > > propertyConditions ,
47+ IReadOnlyCollection < Func < T , object > > constructorParameterGetters ,
48+ IReadOnlyDictionary < string , Func < T , object > > propertyValueGetters ,
49+ IReadOnlyDictionary < string , Func < T , object , bool > > hiddenPropertyConditions ,
50+ IReadOnlyDictionary < string , Func < T , object > > hiddenPropertyValueGetters )
3951 : base ( constructorParameterGetters )
4052 {
53+ var typeInfo = typeof ( T ) . GetTypeInfo ( ) ;
54+ var allUsableProperties = new Dictionary < string , PropertyInfo > ( ) ;
55+ var allHiddenProperties = new Dictionary < string , PropertyInfo > ( ) ;
56+ while ( ! typeInfo . Equals ( typeof ( object ) ) )
57+ {
58+ foreach ( var property in typeInfo
59+ . GetProperties ( BindingFlags . Instance | BindingFlags . Public | BindingFlags . DeclaredOnly ) )
60+ {
61+ if ( ! IsUsableProperty ( property )
62+ || ! Equals ( property . SetMethod . GetBaseDefinition ( ) , property . SetMethod ) )
63+ {
64+ continue ;
65+ }
66+
67+ if ( ! allUsableProperties . ContainsKey ( property . Name ) )
68+ {
69+ allUsableProperties [ property . Name ] = property ;
70+ }
71+ else
72+ {
73+ allHiddenProperties [ property . DeclaringType . Name + "." + property . Name ] = property ;
74+ }
75+ }
76+
77+ typeInfo = typeInfo . BaseType . GetTypeInfo ( ) ;
78+ }
79+
4180 propertyConditions = propertyConditions ?? new Dictionary < string , Func < T , object , bool > > ( ) ;
4281 propertyValueGetters = propertyValueGetters ?? new Dictionary < string , Func < T , object > > ( ) ;
43- var referencedPropertyNames = propertyConditions . Keys . Concat ( propertyValueGetters . Keys ) . Distinct ( ) ;
44- var allUsableProperties = typeof ( T ) . GetTypeInfo ( )
45- . GetProperties ( BindingFlags . Instance | BindingFlags . Public ) . Where ( property =>
46- property . GetIndexParameters ( ) . Length == 0
47- && property . CanWrite
48- && property . SetMethod != null
49- && property . SetMethod . IsPublic )
50- . ToDictionary ( p => p . Name ) ;
51-
52- _propertyData = GetProperties ( referencedPropertyNames , allUsableProperties )
53- . Concat ( allUsableProperties . Values . Where ( IsCandidateProperty ) ) . Distinct ( )
54- . Select (
55- p => new PropertyData (
82+
83+ _propertyData =
84+ GetProperties ( propertyConditions . Keys . Concat ( propertyValueGetters . Keys ) . Distinct ( ) ,
85+ allUsableProperties , hidden : false )
86+ . Concat ( allUsableProperties . Values . Where ( IsCandidateProperty ) ) . Distinct ( )
87+ . Select ( p => new PropertyData (
5688 p . Name ,
5789 p . PropertyType ,
58- propertyValueGetters . GetValueOrDefault ( p . Name , CreatePropertyInitializer ( p ) ) ,
90+ p . DeclaringType ,
91+ propertyValueGetters . GetValueOrDefault ( p . Name ,
92+ CreatePropertyValueGetter ( p ) ) ,
5993 propertyConditions . GetValueOrDefault ( p . Name ,
6094 ( o , v ) => ! Equals ( v , GetDefault ( p . PropertyType ) ) ) ) )
61- . ToArray ( ) ;
95+ . ToArray ( ) ;
96+
97+ hiddenPropertyConditions = hiddenPropertyConditions ?? new Dictionary < string , Func < T , object , bool > > ( ) ;
98+ hiddenPropertyValueGetters = hiddenPropertyValueGetters ?? new Dictionary < string , Func < T , object > > ( ) ;
99+
100+ _hiddenPropertyData =
101+ GetProperties ( hiddenPropertyConditions . Keys . Concat ( hiddenPropertyValueGetters . Keys ) . Distinct ( ) ,
102+ allHiddenProperties , hidden : true )
103+ . Concat ( allHiddenProperties . Values . Where ( IsCandidateProperty ) ) . Distinct ( )
104+ . Select ( p => new PropertyData (
105+ p . Name ,
106+ p . PropertyType ,
107+ p . DeclaringType ,
108+ hiddenPropertyValueGetters . GetValueOrDefault ( p . DeclaringType . Name + "." + p . Name ,
109+ CreatePropertyValueGetter ( p ) ) ,
110+ hiddenPropertyConditions . GetValueOrDefault ( p . DeclaringType . Name + "." + p . Name ,
111+ ( o , v ) => ! Equals ( v , GetDefault ( p . PropertyType ) ) ) ) )
112+ . ToArray ( ) ;
62113 }
63114
64115 protected override bool GenerateEmptyArgumentList => false ;
65116
66- private IEnumerable < PropertyInfo > GetProperties ( IEnumerable < string > propertyNames ,
67- Dictionary < string , PropertyInfo > allProperties )
117+ private IEnumerable < PropertyInfo > GetProperties (
118+ IEnumerable < string > propertyNames ,
119+ Dictionary < string , PropertyInfo > allProperties ,
120+ bool hidden )
68121 {
69122 foreach ( var propertyName in propertyNames )
70123 {
71124 if ( ! allProperties . TryGetValue ( propertyName , out var property ) )
72125 {
126+ if ( hidden )
127+ {
128+ throw new InvalidOperationException (
129+ $ "The type { typeof ( T ) } does not have a hidden public nonstatic writable property { propertyName } ") ;
130+ }
131+
73132 throw new InvalidOperationException (
74133 $ "The type { typeof ( T ) } does not have a public nonstatic writable property { propertyName } ") ;
75134 }
76135 yield return property ;
77136 }
78137 }
79138
80- public override ExpressionSyntax GetCreation ( object obj ) => GetObjectCreationExpression ( ( T ) obj ) ;
139+ public override ExpressionSyntax GetCreation ( object obj )
140+ {
141+ var typedObject = ( T ) obj ;
142+ var objectParameter = IdentifierName ( "o" ) ;
143+ var hiddenPropertyInitializers = _hiddenPropertyData
144+ . Where ( p => p . PropertyCondition ( typedObject , p . PropertyValueGetter ( typedObject ) ) )
145+ . Select ( p => ( StatementSyntax ) ExpressionStatement ( AssignmentExpression (
146+ SyntaxKind . SimpleAssignmentExpression ,
147+ MemberAccessExpression (
148+ SyntaxKind . SimpleMemberAccessExpression ,
149+ ParenthesizedExpression ( CastExpression ( GetTypeSyntax ( p . DeclaringType ) , objectParameter ) ) ,
150+ IdentifierName ( p . PropertyName ) ) ,
151+ GetCreationExpression ( p . PropertyValueGetter ( typedObject ) ) ) ) )
152+ . ToList ( ) ;
153+
154+ var objectCreationExpression = GetObjectCreationExpression ( typedObject ) ;
155+ if ( hiddenPropertyInitializers . Count == 0 )
156+ {
157+ return objectCreationExpression ;
158+ }
159+
160+ hiddenPropertyInitializers . Add ( ReturnStatement ( objectParameter ) ) ;
161+ return InvocationExpression (
162+ ParenthesizedExpression (
163+ CastExpression (
164+ GenericName ( Identifier ( "Func" ) ) . WithTypeArgumentList (
165+ TypeArgumentList (
166+ SeparatedList < TypeSyntax > ( new SyntaxNodeOrToken [ ]
167+ {
168+ GetTypeSyntax ( Type ) , Token ( SyntaxKind . CommaToken ) ,
169+ GetTypeSyntax ( Type )
170+ } ) ) ) ,
171+ ParenthesizedExpression ( SimpleLambdaExpression ( Parameter ( Identifier ( "o" ) ) ,
172+ Block ( hiddenPropertyInitializers ) ) ) ) ) )
173+ . WithArgumentList ( ArgumentList ( SingletonSeparatedList ( Argument ( objectCreationExpression ) ) ) ) ;
174+ }
81175
82176 protected override ObjectCreationExpressionSyntax GetObjectCreationExpression ( T obj )
83177 => base . GetObjectCreationExpression ( obj )
84178 . WithInitializer ( AddNewLine (
85- SyntaxFactory . InitializerExpression (
179+ InitializerExpression (
86180 SyntaxKind . ObjectInitializerExpression ,
87- SyntaxFactory . SeparatedList < ExpressionSyntax > (
181+ SeparatedList < ExpressionSyntax > (
88182 ToCommaSeparatedList ( _propertyData
89183 . Where ( p => p . PropertyCondition ( obj , p . PropertyValueGetter ( obj ) ) )
90- . Select ( p => SyntaxFactory . AssignmentExpression (
184+ . Select ( p => AssignmentExpression (
91185 SyntaxKind . SimpleAssignmentExpression ,
92- SyntaxFactory . IdentifierName ( p . PropertyName ) ,
186+ IdentifierName ( p . PropertyName ) ,
93187 GetCreationExpression ( p . PropertyValueGetter ( obj ) ) ) ) ) ) ) ) ) ;
94188
95- protected static Func < T , object > CreatePropertyInitializer ( PropertyInfo property )
189+ protected static Func < T , object > CreatePropertyValueGetter ( PropertyInfo property )
96190 {
97191 var objectParameter = Expression . Parameter ( typeof ( T ) , name : "o" ) ;
98192 return Expression . Lambda < Func < T , object > > (
@@ -108,17 +202,20 @@ protected class PropertyData
108202 public PropertyData (
109203 string propertyName ,
110204 Type propertyType ,
205+ Type declaringType ,
111206 Func < T , object > propertyValueGetter ,
112207 Func < T , object , bool > propertyCondition )
113208 {
114209 PropertyName = propertyName ;
115210 PropertyType = propertyType ;
211+ DeclaringType = declaringType ;
116212 PropertyValueGetter = propertyValueGetter ;
117213 PropertyCondition = propertyCondition ;
118214 }
119215
120216 public string PropertyName { get ; }
121217 public Type PropertyType { get ; }
218+ public Type DeclaringType { get ; }
122219 public Func < T , object > PropertyValueGetter { get ; }
123220 public Func < T , object , bool > PropertyCondition { get ; }
124221 }
0 commit comments