@@ -20,6 +20,8 @@ public sealed class PageModel : TextContentModel
2020
2121 private static readonly string [ ] CUSTOM_ATTRIBUTE_PREFIX_SEPARATOR = { "." } ;
2222 private static readonly string [ ] CUSTOM_ATTRIBUTE_PREFIX = { "Custom" } ;
23+
24+ private static readonly string [ ] MODEL_ATTRIBUTE_PREFIX = { "Model" } ;
2325
2426 public string Title { get ; set ; }
2527 public string Url { get ; set ; }
@@ -39,11 +41,17 @@ public sealed class PageModel : TextContentModel
3941 /// Raw content after separator
4042 /// </summary>
4143 public string Content { get ; set ; }
44+
45+ public dynamic Model { get ; set ; }
4246
43- public override void Parse ( )
47+ public PageModel ( )
4448 {
4549 Custom = new ExpandoObject ( ) ;
46-
50+ Model = new ExpandoObject ( ) ;
51+ }
52+
53+ public override void Parse ( )
54+ {
4755 var lines = ContentRaw . Split ( END_OF_LINE , StringSplitOptions . None ) ;
4856
4957 var attributeLines = lines
@@ -57,7 +65,18 @@ public override void Parse()
5765
5866 var contentLines = lines . SkipWhile ( x => ! SEPARATOR . Equals ( x ) ) . Skip ( 1 ) ;
5967 ParseContent ( contentLines ) ;
60-
68+ }
69+
70+ public void MapDataToModel ( ExpandoObject data )
71+ {
72+ if ( Model is ExpandoObject )
73+ {
74+ MapDataToPageModel ( Model , data ) ;
75+ }
76+ else
77+ {
78+ Model = getValueFromDataObject ( Model , data ) ;
79+ }
6180 }
6281
6382 private void ParseAttributeLine ( string line )
@@ -100,6 +119,12 @@ private void ParseAttributeLine(string line)
100119 return ;
101120 }
102121
122+ if ( MODEL_ATTRIBUTE_PREFIX . Any ( x => x . Equals ( attributeName , StringComparison . OrdinalIgnoreCase ) ) )
123+ {
124+ Model = attributeValue ;
125+ return ;
126+ }
127+
103128 if ( CUSTOM_ATTRIBUTE_PREFIX_SEPARATOR . Any ( x => attributeName . Contains ( x ) ) )
104129 {
105130 var customAttributeNames = attributeName . Split ( CUSTOM_ATTRIBUTE_PREFIX_SEPARATOR , StringSplitOptions . RemoveEmptyEntries ) ;
@@ -109,38 +134,106 @@ private void ParseAttributeLine(string line)
109134 }
110135 else
111136 {
112- throw new ArgumentException ( $ "Unknown name format of custom attribute \" { attributeName } \" at line \" { line } \" ") ;
137+ if ( customAttributeNames . Length >= 2 && MODEL_ATTRIBUTE_PREFIX . Any ( x =>
138+ x . Equals ( customAttributeNames [ 0 ] , StringComparison . InvariantCultureIgnoreCase ) ) )
139+ {
140+ ParseCustomAttribute ( Model , customAttributeNames . Skip ( 1 ) , attributeValue ) ;
141+ }
142+ else
143+ {
144+ throw new ArgumentException (
145+ $ "Unknown name format of custom attribute \" { attributeName } \" at line \" { line } \" ") ;
146+ }
113147 }
114-
148+
115149 return ;
116150 }
151+
117152
118153 throw new ArgumentException ( $ "Unknown attribute \" { attributeName } \" at line \" { line } \" ") ;
119154 }
120155
121156 private void ParseCustomAttribute ( ExpandoObject obj , IEnumerable < string > attributeNames , string attributeValue )
122157 {
123- ICollection < KeyValuePair < string , object > > node = obj ;
158+ IDictionary < string , object > node = obj ;
124159 var currentSection = attributeNames . First ( ) ;
125- object currentValue ;
126-
160+
127161 if ( attributeNames . Count ( ) > 1 )
128162 {
129- var subNode = new ExpandoObject ( ) ;
130- currentValue = subNode ;
131- ParseCustomAttribute ( subNode , attributeNames . Skip ( 1 ) , attributeValue ) ;
163+ ExpandoObject currentNode ;
164+ if ( node . ContainsKey ( currentSection ) )
165+ {
166+ currentNode = node [ currentSection ] as ExpandoObject ;
167+ }
168+ else
169+ {
170+ currentNode = new ExpandoObject ( ) ;
171+ node . Add ( new KeyValuePair < string , object > ( currentSection , currentNode ) ) ;
172+ }
173+
174+ ParseCustomAttribute ( currentNode , attributeNames . Skip ( 1 ) , attributeValue ) ;
132175 }
133176 else
134177 {
135- currentValue = attributeValue ;
178+ node . Add ( new KeyValuePair < string , object > ( currentSection , attributeValue ) ) ;
136179 }
137-
138- node . Add ( new KeyValuePair < string , object > ( currentSection , currentValue ) ) ;
139180 }
140181
141182 private void ParseContent ( IEnumerable < string > lines )
142183 {
143184 Content = string . Join ( "\r \n " , lines ) ;
144185 }
186+
187+ private void MapDataToPageModel ( ExpandoObject model , ExpandoObject data )
188+ {
189+ IDictionary < string , object > modelDict = model ;
190+ Dictionary < string , object > objectToIterate = model . ToDictionary ( k => k . Key , v => v . Value ) ;
191+ foreach ( var keyValuePair in objectToIterate )
192+ {
193+ if ( keyValuePair . Value is ExpandoObject expObject )
194+ {
195+ MapDataToPageModel ( expObject , data ) ;
196+ }
197+ else
198+ {
199+ modelDict [ keyValuePair . Key ] = getValueFromDataObject ( keyValuePair . Value as string , data ) ;
200+ }
201+ }
202+ }
203+
204+ private object getValueFromDataObject ( string path , ExpandoObject data )
205+ {
206+ var attributeNames = path . Split ( '.' ) ;
207+ if ( attributeNames . Length == 0 )
208+ {
209+ return null ;
210+ }
211+
212+ if ( attributeNames . First ( ) == "Data" )
213+ {
214+ if ( attributeNames . Length == 1 )
215+ {
216+ return data ;
217+ }
218+
219+ attributeNames = attributeNames . Skip ( 1 ) . ToArray ( ) ;
220+ }
221+
222+ object value = data ;
223+ foreach ( var attribute in attributeNames )
224+ {
225+ IDictionary < string , object > dict = value as IDictionary < string , object > ;
226+
227+ if ( dict == null )
228+ {
229+ dict = value . GetType ( ) . GetProperties ( )
230+ . ToDictionary ( x => x . Name , x => x . GetValue ( value , null ) ) ;
231+ }
232+
233+ value = dict [ attribute ] ;
234+ }
235+
236+ return value ;
237+ }
145238 }
146239}
0 commit comments