@@ -329,63 +329,110 @@ private void writeFilteredObjectGson(JsonWriter jsonWriter, Object retObj,
329329 }
330330
331331 /**
332- * Serialize a nested field (Collection, Map, or single POJO) with only
333- * the specified sub-fields. GSON equivalent of the Moshi
334- * {@code writeFilteredNested} method .
332+ * Serialize a nested field with recursive dot-notation support (GSON).
333+ * Mirrors the Moshi {@code writeFilteredNested} — parses sub-fields
334+ * for dots at each level and recurses into Maps, Collections, and POJOs .
335335 */
336336 private void writeFilteredNestedGson (JsonWriter jsonWriter , Object value ,
337337 Set <String > subFields , Gson gson ,
338338 java .util .Map <Class <?>, List <Field >> fieldCache )
339339 throws IOException {
340340
341+ // Parse sub-fields into immediate keeps and deeper specs
342+ Set <String > immediateKeep = new LinkedHashSet <>();
343+ java .util .Map <String , Set <String >> deeperSpecs = new java .util .LinkedHashMap <>();
344+ for (String spec : subFields ) {
345+ int dot = spec .indexOf ('.' );
346+ if (dot > 0 && dot < spec .length () - 1 ) {
347+ String container = spec .substring (0 , dot );
348+ String remainder = spec .substring (dot + 1 );
349+ immediateKeep .add (container );
350+ deeperSpecs .computeIfAbsent (container , k -> new LinkedHashSet <>())
351+ .add (remainder );
352+ } else {
353+ immediateKeep .add (spec );
354+ }
355+ }
356+
341357 if (value instanceof java .util .Collection ) {
342358 jsonWriter .beginArray ();
343359 for (Object element : (java .util .Collection <?>) value ) {
344360 if (element == null ) {
345361 jsonWriter .nullValue ();
346- } else {
347- writeFilteredSingleObjectGson (jsonWriter , element , subFields ,
362+ } else if (element instanceof java .util .Map ) {
363+ writeFilteredMapGson (jsonWriter , (java .util .Map <?, ?>) element ,
364+ immediateKeep , deeperSpecs , gson , fieldCache );
365+ } else if (element instanceof java .util .Collection ) {
366+ writeFilteredNestedGson (jsonWriter , element , subFields ,
348367 gson , fieldCache );
368+ } else {
369+ writeFilteredPojoGson (jsonWriter , element ,
370+ immediateKeep , deeperSpecs , gson , fieldCache );
349371 }
350372 }
351373 jsonWriter .endArray ();
352374 } else if (value instanceof java .util .Map ) {
353- jsonWriter .beginObject ();
354- for (java .util .Map .Entry <?, ?> entry : ((java .util .Map <?, ?>) value ).entrySet ()) {
355- String key = String .valueOf (entry .getKey ());
356- if (subFields .contains (key )) {
357- jsonWriter .name (key );
358- gson .toJson (entry .getValue (), Object .class , jsonWriter );
359- }
360- }
361- jsonWriter .endObject ();
375+ writeFilteredMapGson (jsonWriter , (java .util .Map <?, ?>) value ,
376+ immediateKeep , deeperSpecs , gson , fieldCache );
362377 } else if (value .getClass ().getName ().startsWith ("java.lang." )) {
363378 gson .toJson (value , value .getClass (), jsonWriter );
364379 } else {
365- writeFilteredSingleObjectGson (jsonWriter , value , subFields , gson , fieldCache );
380+ writeFilteredPojoGson (jsonWriter , value ,
381+ immediateKeep , deeperSpecs , gson , fieldCache );
366382 }
367383 }
368384
369385 /**
370- * Serialize a single object with only the specified fields using GSON.
371- * Inner loop of nested filtering — called once per collection element .
386+ * Serialize a Map with recursive field filtering ( GSON) .
387+ * Mirrors the Moshi {@code writeFilteredMap} .
372388 */
373- private void writeFilteredSingleObjectGson (JsonWriter jsonWriter , Object obj ,
374- Set <String > allowedFields , Gson gson ,
375- java .util .Map <Class <?>, List <Field >> fieldCache )
389+ private void writeFilteredMapGson (JsonWriter jsonWriter , java .util .Map <?, ?> map ,
390+ Set <String > immediateKeep ,
391+ java .util .Map <String , Set <String >> deeperSpecs ,
392+ Gson gson ,
393+ java .util .Map <Class <?>, List <Field >> fieldCache )
394+ throws IOException {
395+
396+ jsonWriter .beginObject ();
397+ for (java .util .Map .Entry <?, ?> entry : map .entrySet ()) {
398+ String key = String .valueOf (entry .getKey ());
399+ if (!immediateKeep .contains (key )) continue ;
400+
401+ jsonWriter .name (key );
402+ Object entryValue = entry .getValue ();
403+ Set <String > deeper = deeperSpecs .get (key );
404+ if (deeper != null && entryValue != null ) {
405+ writeFilteredNestedGson (jsonWriter , entryValue , deeper , gson , fieldCache );
406+ } else if (entryValue == null ) {
407+ jsonWriter .nullValue ();
408+ } else {
409+ gson .toJson (entryValue , Object .class , jsonWriter );
410+ }
411+ }
412+ jsonWriter .endObject ();
413+ }
414+
415+ /**
416+ * Serialize a POJO with recursive field filtering (GSON).
417+ * Mirrors the Moshi {@code writeFilteredPojo}.
418+ */
419+ private void writeFilteredPojoGson (JsonWriter jsonWriter , Object pojo ,
420+ Set <String > immediateKeep ,
421+ java .util .Map <String , Set <String >> deeperSpecs ,
422+ Gson gson ,
423+ java .util .Map <Class <?>, List <Field >> fieldCache )
376424 throws IOException {
377425
378426 List <Field > fields = fieldCache .computeIfAbsent (
379- obj .getClass (), JSONStreamingMessageFormatter ::getAllFields );
427+ pojo .getClass (), JSONStreamingMessageFormatter ::getAllFields );
380428 jsonWriter .beginObject ();
381429 for (Field field : fields ) {
382- if (!allowedFields .contains (field .getName ())) {
383- continue ;
384- }
430+ if (!immediateKeep .contains (field .getName ())) continue ;
431+
385432 Object value ;
386433 try {
387434 field .setAccessible (true );
388- value = field .get (obj );
435+ value = field .get (pojo );
389436 } catch (IllegalAccessException | SecurityException e ) {
390437 log .warn ("Cannot access field "
391438 + field .getDeclaringClass ().getName ().replaceAll ("[\r \n ]" , "_" )
@@ -394,7 +441,10 @@ private void writeFilteredSingleObjectGson(JsonWriter jsonWriter, Object obj,
394441 continue ;
395442 }
396443 jsonWriter .name (field .getName ());
397- if (value == null ) {
444+ Set <String > deeper = deeperSpecs != null ? deeperSpecs .get (field .getName ()) : null ;
445+ if (deeper != null && value != null ) {
446+ writeFilteredNestedGson (jsonWriter , value , deeper , gson , fieldCache );
447+ } else if (value == null ) {
398448 jsonWriter .nullValue ();
399449 } else {
400450 gson .toJson (value , field .getGenericType (), jsonWriter );
0 commit comments