@@ -162,6 +162,8 @@ public QueryPredicate deserialize(JsonElement json, Type type, JsonDeserializati
162162 case OPERATION :
163163 return gson .fromJson (json , QueryPredicateOperation .class );
164164 case GROUP :
165+ // We need to manually deserialize Groups to ensure we handle nested groups
166+ // and update _types correctly.
165167 return deserializeQueryPredicateGroup (jsonObject );
166168 case ALL :
167169 return gson .fromJson (json , MatchAllQueryPredicate .class );
@@ -182,6 +184,8 @@ public JsonElement serialize(QueryPredicate predicate, Type type, JsonSerializat
182184 JsonElement json ;
183185 PredicateType predicateType ;
184186 if (predicate instanceof QueryPredicateGroup ) {
187+ // We need to manually serialize Groups to ensure we handle nested groups and
188+ // update _types correctly.
185189 predicateType = PredicateType .GROUP ;
186190 json = serializeQueryPredicateGroup ((QueryPredicateGroup ) predicate , context );
187191 } else {
@@ -201,19 +205,58 @@ public JsonElement serialize(QueryPredicate predicate, Type type, JsonSerializat
201205 return jsonObject ;
202206 }
203207
208+ /**
209+ * Serializes a QueryPredicateGroup to JSON format.
210+ * <p>
211+ * This method is necessary because QueryPredicateGroup contains nested QueryPredicate objects
212+ * that need to be recursively serialized. We cannot use context.serialize() directly on
213+ * QueryPredicateGroup because:
214+ * 1. Using context.serialize() would cause infinite recursion back to this adapter
215+ * 2. We need to manually construct the JSON structure with proper "_type" fields
216+ * <p>
217+ * The method handles:
218+ * - Serializing the group type (AND, OR, NOT)
219+ * - Recursively serializing each nested predicate in the predicates array
220+ * - Maintaining the correct JSON structure expected by the deserializer
221+ *
222+ * @param group The QueryPredicateGroup to serialize
223+ * @param context The serialization context for handling nested QueryOperator objects
224+ * @return JsonElement representing the serialized group
225+ */
204226 private JsonElement serializeQueryPredicateGroup (QueryPredicateGroup group , JsonSerializationContext context ) {
205227 JsonObject jsonObject = new JsonObject ();
206228 jsonObject .addProperty ("type" , group .type ().name ());
207229
208230 JsonArray predicatesArray = new JsonArray ();
209231 for (QueryPredicate predicate : group .predicates ()) {
232+ // Recursively serialize nested predicates using this adapter
210233 predicatesArray .add (serialize (predicate , QueryPredicate .class , context ));
211234 }
212235 jsonObject .add ("predicates" , predicatesArray );
213236
214237 return jsonObject ;
215238 }
216239
240+ /**
241+ * Deserializes a JSON object into a QueryPredicateGroup.
242+ * <p>
243+ * This method is necessary because QueryPredicateGroup contains nested QueryPredicate objects
244+ * that need to be recursively deserialized. We cannot use context.deserialize() directly
245+ * because:
246+ * 1. Using context.deserialize() would cause infinite recursion back to this adapter
247+ * 2. We need to manually parse the JSON structure and handle nested predicates
248+ * <p>
249+ * The method handles:
250+ * - Parsing the group type (AND, OR, NOT) from the "type" field
251+ * - Recursively deserializing each predicate in the "predicates" array
252+ * - Creating the QueryPredicateGroup with the correct constructor (no builder available)
253+ * <p>
254+ * This is critical for DataStore sync expressions that contain nested predicate groups,
255+ * which caused the "Interfaces can't be instantiated" error before this fix.
256+ *
257+ * @param jsonObject The JSON object containing the group data
258+ * @return QueryPredicateGroup instance with all nested predicates deserialized
259+ */
217260 private QueryPredicateGroup deserializeQueryPredicateGroup (JsonObject jsonObject ) {
218261 QueryPredicateGroup .Type type = QueryPredicateGroup .Type .valueOf (
219262 jsonObject .get ("type" ).getAsString ()
@@ -222,10 +265,13 @@ private QueryPredicateGroup deserializeQueryPredicateGroup(JsonObject jsonObject
222265 List <QueryPredicate > predicates = new ArrayList <>();
223266 JsonArray predicatesArray = jsonObject .getAsJsonArray ("predicates" );
224267 for (JsonElement predicateElement : predicatesArray ) {
268+ // Recursively deserialize nested predicates using this adapter
269+ // Note: Passing null for context since we handle recursion manually
225270 QueryPredicate predicate = deserialize (predicateElement , QueryPredicate .class , null );
226271 predicates .add (predicate );
227272 }
228273
274+ // Use constructor since QueryPredicateGroup doesn't have a builder
229275 return new QueryPredicateGroup (type , predicates );
230276 }
231277 }
0 commit comments