@@ -156,53 +156,93 @@ static Dictionary<string, string> GetDiffAsDictionary(Type type, object current,
156
156
157
157
foreach ( var field in type . GetSerializableFields ( ) )
158
158
{
159
- var t = field . FieldType ;
160
- try
161
- {
162
- if ( t . GetCustomAttribute < ObsoleteAttribute > ( ) != null || typeof ( ScriptableObject ) . IsAssignableFrom ( t ) )
163
- continue ;
159
+ var fieldType = field . FieldType ;
160
+ if ( ! IsFieldIgnored ( fieldType ) )
161
+ AddDiff ( current , defaults , field , fieldType , diff ) ;
162
+ }
164
163
165
- var valueCurrent = current != null ? field . GetValue ( current ) : null ;
166
- var valueDefault = defaults != null ? field . GetValue ( defaults ) : null ;
164
+ return diff ;
165
+ }
167
166
168
- if ( t == typeof ( string ) )
169
- {
170
- var stringCurrent = ( string ) valueCurrent ;
171
- var stringDefault = ( string ) valueDefault ;
172
- if ( stringCurrent != stringDefault )
173
- {
174
- diff [ field . Name ] = stringCurrent ;
175
- }
176
- }
177
- else if ( t . IsPrimitive || t . IsEnum )
178
- {
179
- if ( ! valueCurrent . Equals ( valueDefault ) )
180
- diff [ field . Name ] = ConvertPrimitiveWithInvariants ( valueCurrent ) ;
181
- }
182
- else if ( t . IsArray && valueCurrent is IList valueCurrentList )
183
- {
184
- if ( AreArraysDifferent ( valueCurrentList , valueDefault as IList ) )
185
- diff [ field . Name ] = valueCurrentList . DumpValues ( ) ;
186
- }
187
- else if ( t . IsClass || t . IsValueType )
188
- {
189
- if ( valueCurrent is IEnumerable ea )
190
- continue ; // List<T> not supported
167
+ private static void AddDiff ( object current , object defaults , FieldInfo field , Type fieldType , Dictionary < string , string > diff )
168
+ {
169
+ try
170
+ {
171
+ var valueCurrent = current != null ? field . GetValue ( current ) : null ;
172
+ var valueDefault = defaults != null ? field . GetValue ( defaults ) : null ;
173
+ AddIfDifferent ( field , fieldType , diff , valueCurrent , valueDefault ) ;
174
+ }
175
+ catch ( Exception ex )
176
+ {
177
+ Debug . LogError ( $ "Exception found while parsing { field } , { ex } ") ;
178
+ }
179
+ }
191
180
192
- var subDiff = GetDiffAsDictionary ( t , valueCurrent , valueDefault ) ;
193
- foreach ( var d in subDiff )
194
- {
195
- diff [ field . Name + "." + d . Key ] = d . Value ;
196
- }
181
+ private static void AddIfDifferent ( FieldInfo field , Type fieldType , Dictionary < string , string > diff , object valueCurrent , object valueDefault )
182
+ {
183
+ if ( ! AreValuesEqual ( fieldType , valueCurrent , valueDefault ) )
184
+ {
185
+ if ( IsComplexType ( fieldType ) )
186
+ {
187
+ var subDiff = GetDiffAsDictionary ( fieldType , valueCurrent , valueDefault ) ;
188
+ foreach ( var d in subDiff )
189
+ {
190
+ diff [ $ "{ field . Name } .{ d . Key } "] = d . Value ;
197
191
}
198
192
}
199
- catch ( Exception ex )
193
+ else
200
194
{
201
- Debug . LogError ( $ "Exception found while parsing { field } , { ex } " ) ;
195
+ diff [ field . Name ] = ConvertValueToString ( valueCurrent ) ;
202
196
}
203
197
}
198
+ }
204
199
205
- return diff ;
200
+ static bool IsFieldIgnored ( Type fieldType )
201
+ {
202
+ return fieldType . GetCustomAttribute < ObsoleteAttribute > ( ) != null || typeof ( ScriptableObject ) . IsAssignableFrom ( fieldType ) ;
203
+ }
204
+
205
+ internal static bool AreValuesEqual ( Type fieldType , object valueCurrent , object valueDefault )
206
+ {
207
+ if ( fieldType == typeof ( string ) )
208
+ return ( string ) valueCurrent == ( string ) valueDefault ;
209
+
210
+ if ( fieldType . IsPrimitive || fieldType . IsEnum )
211
+ return valueCurrent . Equals ( valueDefault ) ;
212
+
213
+ if ( fieldType . IsArray && valueCurrent is IList currentList )
214
+ return ! AreArraysDifferent ( currentList , valueDefault as IList ) ;
215
+
216
+ if ( valueCurrent == null && valueDefault == null )
217
+ return true ;
218
+
219
+ return valueDefault ? . Equals ( valueCurrent ) ?? valueCurrent ? . Equals ( null ) ?? false ;
220
+ }
221
+
222
+ internal static bool IsComplexType ( Type fieldType )
223
+ {
224
+ // Primitive types and enums are not considered complex
225
+ if ( fieldType . IsPrimitive || fieldType . IsEnum )
226
+ return false ;
227
+
228
+ // String is considered a primitive type for our purposes
229
+ if ( fieldType == typeof ( string ) )
230
+ return false ;
231
+
232
+ // Arrays can be converted to string easy without sub-elements
233
+ if ( fieldType . IsArray )
234
+ return false ;
235
+
236
+ // Value types (structs) that are not primitive are considered complex
237
+ // Classes are considered complex types
238
+ return fieldType . IsValueType || fieldType . IsClass ;
239
+ }
240
+
241
+ static string ConvertValueToString ( object value )
242
+ {
243
+ if ( value == null ) return null ;
244
+ if ( value is IList list ) return list . DumpValues ( ) ;
245
+ return ConvertPrimitiveWithInvariants ( value ) ;
206
246
}
207
247
208
248
static string ConvertPrimitiveWithInvariants ( object obj )
@@ -212,17 +252,49 @@ static string ConvertPrimitiveWithInvariants(object obj)
212
252
return obj . ToString ( ) ;
213
253
}
214
254
215
- static string [ ] ToStringArray ( Dictionary < string , string > diff )
255
+ static string [ ] ToStringArray ( Dictionary < string , string > diff , string format = null )
216
256
{
217
257
var changedSettings = new string [ diff . Count ] ;
218
258
259
+ if ( string . IsNullOrEmpty ( format ) )
260
+ format = @"{{""{0}"":""{1}""}}" ;
261
+
219
262
int i = 0 ;
220
263
foreach ( var d in diff )
221
- changedSettings [ i ++ ] = $@ "{{""{ d.Key}"":"" { d . Value } ""}}" ;
264
+ changedSettings [ i ++ ] = string . Format ( format , d . Key , d . Value ) ;
222
265
223
266
return changedSettings ;
224
267
}
225
268
269
+ private static string [ ] EnumerableToNestedColumn < T > ( [ DisallowNull ] this IEnumerable collection )
270
+ {
271
+ using ( ListPool < string > . Get ( out var tmp ) )
272
+ {
273
+ foreach ( var element in collection )
274
+ {
275
+ string [ ] elementColumns = ToStringArray ( DumpValues ( element . GetType ( ) , element ) , @"""{0}"":""{1}""" ) ;
276
+ tmp . Add ( "{" + string . Join ( ", " , elementColumns ) + "}" ) ;
277
+ }
278
+
279
+ return tmp . ToArray ( ) ;
280
+ }
281
+ }
282
+
283
+ private static string [ ] ToNestedColumnSimplify < T > ( [ DisallowNull ] this T current )
284
+ where T : new ( )
285
+ {
286
+ var type = current . GetType ( ) ;
287
+
288
+ if ( typeof ( UnityEngine . Object ) . IsAssignableFrom ( typeof ( T ) ) )
289
+ {
290
+ var instance = ScriptableObject . CreateInstance ( type ) ;
291
+ ToStringArray ( GetDiffAsDictionary ( type , current , instance ) ) ;
292
+ ScriptableObject . DestroyImmediate ( instance ) ;
293
+ }
294
+
295
+ return ToStringArray ( GetDiffAsDictionary ( type , current , new T ( ) ) ) ;
296
+ }
297
+
226
298
/// <summary>
227
299
/// Obtains the Serialized fields and values in form of nested columns for BigQuery
228
300
/// https://cloud.google.com/bigquery/docs/nested-repeated
@@ -238,28 +310,13 @@ public static string[] ToNestedColumn<T>([DisallowNull] this T current, bool com
238
310
if ( current == null )
239
311
throw new ArgumentNullException ( nameof ( current ) ) ;
240
312
241
- var type = current . GetType ( ) ;
313
+ if ( current is IEnumerable currentAsEnumerable )
314
+ return EnumerableToNestedColumn < T > ( currentAsEnumerable ) ;
242
315
243
- Dictionary < string , string > diff ;
244
316
if ( compareAndSimplifyWithDefault )
245
- {
246
- if ( typeof ( UnityEngine . Object ) . IsAssignableFrom ( typeof ( T ) ) )
247
- {
248
- var instance = ScriptableObject . CreateInstance ( type ) ;
249
- diff = GetDiffAsDictionary ( type , current , instance ) ;
250
- ScriptableObject . DestroyImmediate ( instance ) ;
251
- }
252
- else
253
- {
254
- diff = GetDiffAsDictionary ( type , current , new T ( ) ) ;
255
- }
256
- }
257
- else
258
- {
259
- diff = DumpValues ( type , current ) ;
260
- }
317
+ return ToNestedColumnSimplify ( current ) ;
261
318
262
- return ToStringArray ( diff ) ;
319
+ return ToStringArray ( DumpValues ( current . GetType ( ) , current ) ) ;
263
320
}
264
321
265
322
/// <summary>
0 commit comments