@@ -156,53 +156,93 @@ static Dictionary<string, string> GetDiffAsDictionary(Type type, object current,
156156
157157 foreach ( var field in type . GetSerializableFields ( ) )
158158 {
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+ }
164163
165- var valueCurrent = current != null ? field . GetValue ( current ) : null ;
166- var valueDefault = defaults != null ? field . GetValue ( defaults ) : null ;
164+ return diff ;
165+ }
167166
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+ }
191180
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 ;
197191 }
198192 }
199- catch ( Exception ex )
193+ else
200194 {
201- Debug . LogError ( $ "Exception found while parsing { field } , { ex } " ) ;
195+ diff [ field . Name ] = ConvertValueToString ( valueCurrent ) ;
202196 }
203197 }
198+ }
204199
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 ) ;
206246 }
207247
208248 static string ConvertPrimitiveWithInvariants ( object obj )
@@ -212,17 +252,49 @@ static string ConvertPrimitiveWithInvariants(object obj)
212252 return obj . ToString ( ) ;
213253 }
214254
215- static string [ ] ToStringArray ( Dictionary < string , string > diff )
255+ static string [ ] ToStringArray ( Dictionary < string , string > diff , string format = null )
216256 {
217257 var changedSettings = new string [ diff . Count ] ;
218258
259+ if ( string . IsNullOrEmpty ( format ) )
260+ format = @"{{""{0}"":""{1}""}}" ;
261+
219262 int i = 0 ;
220263 foreach ( var d in diff )
221- changedSettings [ i ++ ] = $@ "{{""{ d.Key}"":"" { d . Value } ""}}" ;
264+ changedSettings [ i ++ ] = string . Format ( format , d . Key , d . Value ) ;
222265
223266 return changedSettings ;
224267 }
225268
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+
226298 /// <summary>
227299 /// Obtains the Serialized fields and values in form of nested columns for BigQuery
228300 /// https://cloud.google.com/bigquery/docs/nested-repeated
@@ -238,28 +310,13 @@ public static string[] ToNestedColumn<T>([DisallowNull] this T current, bool com
238310 if ( current == null )
239311 throw new ArgumentNullException ( nameof ( current ) ) ;
240312
241- var type = current . GetType ( ) ;
313+ if ( current is IEnumerable currentAsEnumerable )
314+ return EnumerableToNestedColumn < T > ( currentAsEnumerable ) ;
242315
243- Dictionary < string , string > diff ;
244316 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 ) ;
261318
262- return ToStringArray ( diff ) ;
319+ return ToStringArray ( DumpValues ( current . GetType ( ) , current ) ) ;
263320 }
264321
265322 /// <summary>
0 commit comments