This is an automated email from the ASF dual-hosted git repository.

chaokunyang pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/fory.git


The following commit(s) were added to refs/heads/main by this push:
     new a1388d4a2 refactor(java): simplify JSON subtype member writing (#3842)
a1388d4a2 is described below

commit a1388d4a20e52e168d044882271eb874539461e4
Author: Shawn Yang <[email protected]>
AuthorDate: Tue Jul 14 18:00:05 2026 +0530

    refactor(java): simplify JSON subtype member writing (#3842)
    
    ## Why?
    
    
    
    ## What does this PR do?
    
    
    
    ## Related issues
    
    
    
    ## AI Contribution Checklist
    
    
    
    - [ ] Substantial AI assistance was used in this PR: `yes` / `no`
    - [ ] If `yes`, I included a completed [AI Contribution
    
Checklist](https://github.com/apache/fory/blob/main/AI_POLICY.md#9-contributor-checklist-for-ai-assisted-prs)
    in this PR description and the required `AI Usage Disclosure`.
    - [ ] If `yes`, my PR description includes the required `ai_review`
    summary and screenshot evidence or equivalent persisted links of the
    final clean AI review results from both fresh reviewers described in
    `AI_POLICY.md`, the Fory-guided reviewer and the independent general
    reviewer, on the current PR diff or current HEAD after the latest code
    changes.
    
    
    
    ## Does this PR introduce any user-facing change?
    
    
    
    - [ ] Does this PR introduce any public API change?
    - [ ] Does this PR introduce any binary protocol compatibility change?
    
    ## Benchmark
---
 .../apache/fory/json/codec/ClosedSubtypeCodec.java |  26 +---
 .../apache/fory/json/codec/JsonSubTypesInfo.java   |   7 +-
 .../org/apache/fory/json/codec/ObjectCodec.java    |  88 +++++++------
 .../apache/fory/json/codec/StringObjectWriter.java |  41 ------
 .../apache/fory/json/codec/Utf8ObjectWriter.java   |  33 -----
 .../org/apache/fory/json/codegen/JsonCodegen.java  |  53 +-------
 .../fory/json/codegen/JsonWriterCodegen.java       | 144 ++-------------------
 .../fory/json/codegen/StringWriterCodegen.java     |   8 +-
 .../fory/json/codegen/Utf8WriterCodegen.java       |   8 +-
 .../apache/fory/json/resolver/JsonTypeInfo.java    |   5 +-
 .../fory/json/resolver/JsonTypeResolver.java       | 114 +---------------
 .../org/apache/fory/json/JsonAnyPropertyTest.java  |  32 ++++-
 .../apache/fory/json/JsonAsyncCompilationTest.java |  64 ++-------
 .../org/apache/fory/json/JsonSubTypesTest.java     |  37 ++----
 14 files changed, 122 insertions(+), 538 deletions(-)

diff --git 
a/java/fory-json/src/main/java/org/apache/fory/json/codec/ClosedSubtypeCodec.java
 
b/java/fory-json/src/main/java/org/apache/fory/json/codec/ClosedSubtypeCodec.java
index aef3b6ac4..faff5041c 100644
--- 
a/java/fory-json/src/main/java/org/apache/fory/json/codec/ClosedSubtypeCodec.java
+++ 
b/java/fory-json/src/main/java/org/apache/fory/json/codec/ClosedSubtypeCodec.java
@@ -106,14 +106,6 @@ public final class ClosedSubtypeCodec implements 
JsonCodec<Object> {
           // needs this parent-local skip table. Nested child values must use 
the canonical table.
           resolver.resolveInlineAnyReaders(this, i, objectCodec, table);
         }
-        // Member writing is a parent-neutral child representation: this codec 
owns the outer
-        // object and discriminator, while the child owns field access, 
omission, Any placement,
-        // and recursive complete-value writes. Keep its generated capability 
in the child slot so
-        // multiple bases can share it without duplicating codegen or 
parent-local writer state.
-        // Readers differ because rereading this parent's discriminator 
changes field
-        // classification, which is why only the inline Any reader binding 
above is parent-local.
-        resolver.stringObjectWriter(objectCodecs[i]);
-        resolver.utf8ObjectWriter(objectCodecs[i]);
       }
       children[i] = child;
     }
@@ -130,7 +122,7 @@ public final class ClosedSubtypeCodec implements 
JsonCodec<Object> {
       writer.writeObjectStart();
       writer.writeRawValue(
           definition.stringSubtypePrefixes[index], 
definition.stringUtf16SubtypePrefixes[index]);
-      stringObjectWriter(index).writeStringMembers(writer, value, 1);
+      objectCodecs[index].writeMembers(writer, value, 1);
       writer.writeObjectEnd();
       return;
     }
@@ -159,7 +151,7 @@ public final class ClosedSubtypeCodec implements 
JsonCodec<Object> {
     if (definition.inclusion == Inclusion.PROPERTY) {
       writer.writeObjectStart();
       writer.writeRawValue(definition.utf8SubtypePrefixes[index]);
-      utf8ObjectWriter(index).writeUtf8Members(writer, value, 1);
+      objectCodecs[index].writeMembers(writer, value, 1);
       writer.writeObjectEnd();
       return;
     }
@@ -290,20 +282,6 @@ public final class ClosedSubtypeCodec implements 
JsonCodec<Object> {
     return index;
   }
 
-  private StringObjectWriter<Object> stringObjectWriter(int index) {
-    StringWriterCodec<Object> writer = children[index].stringWriter();
-    return writer instanceof StringObjectWriter
-        ? (StringObjectWriter<Object>) writer
-        : objectCodecs[index];
-  }
-
-  private Utf8ObjectWriter<Object> utf8ObjectWriter(int index) {
-    Utf8WriterCodec<Object> writer = children[index].utf8Writer();
-    return writer instanceof Utf8ObjectWriter
-        ? (Utf8ObjectWriter<Object>) writer
-        : objectCodecs[index];
-  }
-
   @Internal
   public void setInlineLatin1Reader(int index, Latin1ReaderCodec<Object> 
reader) {
     inlineLatin1Readers[index] = reader;
diff --git 
a/java/fory-json/src/main/java/org/apache/fory/json/codec/JsonSubTypesInfo.java 
b/java/fory-json/src/main/java/org/apache/fory/json/codec/JsonSubTypesInfo.java
index 6a6fa88bd..3963cecb1 100644
--- 
a/java/fory-json/src/main/java/org/apache/fory/json/codec/JsonSubTypesInfo.java
+++ 
b/java/fory-json/src/main/java/org/apache/fory/json/codec/JsonSubTypesInfo.java
@@ -32,9 +32,10 @@ public final class JsonSubTypesInfo {
   final Inclusion inclusion;
   final Class<?>[] classes;
   final JsonSubtypeScanInfo scanInfo;
-  // PROPERTY prefixes contain the complete discriminator member; its member 
writer receives an
-  // initial count of one and owns the following comma. Wrapper prefixes 
include ':' or ',' before
-  // the complete subtype value. No writer adds a separate separator branch on 
the hot path.
+  // PROPERTY prefixes contain the complete discriminator member, so the 
following ObjectCodec
+  // traversal starts with one member already written and owns the next comma. 
Wrapper prefixes
+  // include ':' or ',' before the complete subtype value. No writer adds a 
separate separator
+  // branch on the hot path.
   final byte[][] stringSubtypePrefixes;
   final byte[][] stringUtf16SubtypePrefixes;
   final byte[][] utf8SubtypePrefixes;
diff --git 
a/java/fory-json/src/main/java/org/apache/fory/json/codec/ObjectCodec.java 
b/java/fory-json/src/main/java/org/apache/fory/json/codec/ObjectCodec.java
index 6bc19d67e..d7c5d7674 100644
--- a/java/fory-json/src/main/java/org/apache/fory/json/codec/ObjectCodec.java
+++ b/java/fory-json/src/main/java/org/apache/fory/json/codec/ObjectCodec.java
@@ -66,7 +66,7 @@ import org.apache.fory.util.record.RecordUtils;
  * Generated code may replace paths independently, but it is built from this 
codec's immutable field
  * metadata and preserves the same null, unknown-field, record, and 
member-discovery semantics.
  */
-public class ObjectCodec<T> implements JsonCodec<T>, StringObjectWriter<T>, 
Utf8ObjectWriter<T> {
+public class ObjectCodec<T> implements JsonCodec<T> {
   private static final int MUTABLE = 0;
   private static final int RECORD = 1;
   private static final int CREATOR = 2;
@@ -1039,20 +1039,39 @@ public class ObjectCodec<T> implements JsonCodec<T>, 
StringObjectWriter<T>, Utf8
 
   final void writeStringObject(StringJsonWriter writer, T value) {
     writer.writeObjectStart();
-    writeStringMembers(writer, value, 0);
+    writeMembers(writer, value, 0);
     writer.writeObjectEnd();
   }
 
-  @Override
-  public final void writeStringMembers(StringJsonWriter writer, T value, int 
written) {
+  final void writeUtf8Object(Utf8JsonWriter writer, T value) {
+    writer.writeObjectStart();
+    writeMembers(writer, value, 0);
+    writer.writeObjectEnd();
+  }
+
+  // ClosedSubtypeCodec owns the open object and discriminator for PROPERTY 
inclusion. Keep this
+  // interpreted traversal package-local instead of publishing partial-object 
writing as a child
+  // codec capability; a complete generated writer cannot safely enter an 
object already in
+  // progress. Only this object layer is interpreted: JsonFieldInfo still 
writes every nested
+  // complete value through its normal codec entry, where ordinary child code 
generation remains
+  // active.
+  final void writeMembers(StringJsonWriter writer, T value, int written) {
+    if (anyInfo != null && anyInfo.writeEnabled()) {
+      writeAnyMembers(writer, value, written);
+      return;
+    }
+    writeFixedMembers(writer, value, written);
+  }
+
+  final void writeMembers(Utf8JsonWriter writer, T value, int written) {
     if (anyInfo != null && anyInfo.writeEnabled()) {
-      writeStringAnyMembers(writer, value, written);
+      writeAnyMembers(writer, value, written);
       return;
     }
-    writeStringFixedMembers(writer, value, written);
+    writeFixedMembers(writer, value, written);
   }
 
-  private void writeStringFixedMembers(StringJsonWriter writer, T value, int 
written) {
+  private void writeFixedMembers(StringJsonWriter writer, T value, int 
written) {
     JsonFieldInfo[] fields = writeFields;
     int length = fields.length;
     int i = 0;
@@ -1077,40 +1096,7 @@ public class ObjectCodec<T> implements JsonCodec<T>, 
StringObjectWriter<T>, Utf8
     }
   }
 
-  private void writeStringAnyMembers(StringJsonWriter writer, T value, int 
written) {
-    int anyIndex = anyInfo.writeIndex;
-    JsonFieldInfo[] fields = writeFields;
-    for (int i = 0; i < anyIndex; i++) {
-      if (fields[i].writeString(writer, value, written)) {
-        written++;
-      }
-    }
-    written =
-        writeStringAny(
-            writer, anyInfo.writeMap(value), 
anyInfo.valueTypeInfo.stringWriter(), written);
-    for (int i = anyIndex; i < fields.length; i++) {
-      if (fields[i].writeString(writer, value, written)) {
-        written++;
-      }
-    }
-  }
-
-  final void writeUtf8Object(Utf8JsonWriter writer, T value) {
-    writer.writeObjectStart();
-    writeUtf8Members(writer, value, 0);
-    writer.writeObjectEnd();
-  }
-
-  @Override
-  public final void writeUtf8Members(Utf8JsonWriter writer, T value, int 
written) {
-    if (anyInfo != null && anyInfo.writeEnabled()) {
-      writeUtf8AnyMembers(writer, value, written);
-      return;
-    }
-    writeUtf8FixedMembers(writer, value, written);
-  }
-
-  private void writeUtf8FixedMembers(Utf8JsonWriter writer, T value, int 
written) {
+  private void writeFixedMembers(Utf8JsonWriter writer, T value, int written) {
     JsonFieldInfo[] fields = writeFields;
     int length = fields.length;
     int i = 0;
@@ -1135,7 +1121,25 @@ public class ObjectCodec<T> implements JsonCodec<T>, 
StringObjectWriter<T>, Utf8
     }
   }
 
-  private void writeUtf8AnyMembers(Utf8JsonWriter writer, T value, int 
written) {
+  private void writeAnyMembers(StringJsonWriter writer, T value, int written) {
+    int anyIndex = anyInfo.writeIndex;
+    JsonFieldInfo[] fields = writeFields;
+    for (int i = 0; i < anyIndex; i++) {
+      if (fields[i].writeString(writer, value, written)) {
+        written++;
+      }
+    }
+    written =
+        writeStringAny(
+            writer, anyInfo.writeMap(value), 
anyInfo.valueTypeInfo.stringWriter(), written);
+    for (int i = anyIndex; i < fields.length; i++) {
+      if (fields[i].writeString(writer, value, written)) {
+        written++;
+      }
+    }
+  }
+
+  private void writeAnyMembers(Utf8JsonWriter writer, T value, int written) {
     int anyIndex = anyInfo.writeIndex;
     JsonFieldInfo[] fields = writeFields;
     for (int i = 0; i < anyIndex; i++) {
diff --git 
a/java/fory-json/src/main/java/org/apache/fory/json/codec/StringObjectWriter.java
 
b/java/fory-json/src/main/java/org/apache/fory/json/codec/StringObjectWriter.java
deleted file mode 100644
index 25ccc4640..000000000
--- 
a/java/fory-json/src/main/java/org/apache/fory/json/codec/StringObjectWriter.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.fory.json.codec;
-
-import org.apache.fory.annotation.Internal;
-import org.apache.fory.json.writer.StringJsonWriter;
-
-/**
- * Child-owned object-member representation used to compose an object around 
that child's members.
- *
- * <p>This refinement is independent of the composing parent: it receives no 
discriminator, base
- * type, subtype index, or reserved name. The child still owns field access, 
omission, property
- * order, Any placement, and recursive complete-value writes, so moving member 
generation into a
- * parent codec would duplicate those child semantics. Implementations also 
remain complete {@link
- * StringWriterCodec} instances for nested and ordinary child values.
- *
- * <p>The caller has already written the opening brace and any preceding 
members. Implementations
- * write only ordinary object members: they must not write null for the 
object, braces, or depth.
- */
-@Internal
-public interface StringObjectWriter<T> extends StringWriterCodec<T> {
-  /** Writes object members after {@code written} members have already been 
emitted. */
-  void writeStringMembers(StringJsonWriter writer, T value, int written);
-}
diff --git 
a/java/fory-json/src/main/java/org/apache/fory/json/codec/Utf8ObjectWriter.java 
b/java/fory-json/src/main/java/org/apache/fory/json/codec/Utf8ObjectWriter.java
deleted file mode 100644
index 858d00803..000000000
--- 
a/java/fory-json/src/main/java/org/apache/fory/json/codec/Utf8ObjectWriter.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.fory.json.codec;
-
-import org.apache.fory.annotation.Internal;
-import org.apache.fory.json.writer.Utf8JsonWriter;
-
-/**
- * UTF-8 counterpart of {@link StringObjectWriter}; the capability is 
child-owned and contains no
- * composing-parent or discriminator state.
- */
-@Internal
-public interface Utf8ObjectWriter<T> extends Utf8WriterCodec<T> {
-  /** Writes object members after {@code written} members have already been 
emitted. */
-  void writeUtf8Members(Utf8JsonWriter writer, T value, int written);
-}
diff --git 
a/java/fory-json/src/main/java/org/apache/fory/json/codegen/JsonCodegen.java 
b/java/fory-json/src/main/java/org/apache/fory/json/codegen/JsonCodegen.java
index f950beb0d..adebf9660 100644
--- a/java/fory-json/src/main/java/org/apache/fory/json/codegen/JsonCodegen.java
+++ b/java/fory-json/src/main/java/org/apache/fory/json/codegen/JsonCodegen.java
@@ -62,8 +62,6 @@ public final class JsonCodegen {
   private final ClassLoader jsonLoader;
   private final Map<Class<?>, Class<?>> stringWriterClasses = new 
ConcurrentHashMap<>();
   private final Map<Class<?>, Class<?>> utf8WriterClasses = new 
ConcurrentHashMap<>();
-  private final Map<Class<?>, Class<?>> stringObjectWriterClasses = new 
ConcurrentHashMap<>();
-  private final Map<Class<?>, Class<?>> utf8ObjectWriterClasses = new 
ConcurrentHashMap<>();
   private final Map<Class<?>, Class<?>> latin1ReaderClasses = new 
ConcurrentHashMap<>();
   private final Map<Class<?>, Class<?>> utf16ReaderClasses = new 
ConcurrentHashMap<>();
   private final Map<Class<?>, Class<?>> utf8ReaderClasses = new 
ConcurrentHashMap<>();
@@ -116,24 +114,6 @@ public final class JsonCodegen {
     return utf8WriterClasses.computeIfAbsent(codec.type(), ignored -> 
buildUtf8Writer(codec));
   }
 
-  @Internal
-  public Class<?> compileStringObjectWriter(ObjectCodec<?> codec) {
-    if (!canCompileWriter(codec)) {
-      return null;
-    }
-    return stringObjectWriterClasses.computeIfAbsent(
-        codec.type(), ignored -> buildStringWriter(codec, true));
-  }
-
-  @Internal
-  public Class<?> compileUtf8ObjectWriter(ObjectCodec<?> codec) {
-    if (!canCompileWriter(codec)) {
-      return null;
-    }
-    return utf8ObjectWriterClasses.computeIfAbsent(
-        codec.type(), ignored -> buildUtf8Writer(codec, true));
-  }
-
   @Internal
   public Class<?> compileLatin1Reader(ObjectCodec<?> codec) {
     if (!canCompileReader(codec)) {
@@ -168,16 +148,6 @@ public final class JsonCodegen {
     return jitId(type, "Utf8Writer");
   }
 
-  @Internal
-  public String stringObjectWriterJITId(Class<?> type) {
-    return jitId(type, "StringObjectWriter");
-  }
-
-  @Internal
-  public String utf8ObjectWriterJITId(Class<?> type) {
-    return jitId(type, "Utf8ObjectWriter");
-  }
-
   @Internal
   public String latin1ReaderJITId(Class<?> type) {
     return jitId(type, "Latin1Reader");
@@ -198,42 +168,31 @@ public final class JsonCodegen {
   }
 
   private Class<?> buildStringWriter(ObjectCodec<?> codec) {
-    return buildStringWriter(codec, false);
-  }
-
-  private Class<?> buildStringWriter(ObjectCodec<?> codec, boolean 
objectMembers) {
     Class<?> type = codec.type();
     String generatedPackage = CodeGenerator.getPackage(type);
-    String className = className(type, objectMembers ? "StringObjectWriter" : 
"StringWriter");
+    String className = className(type, "StringWriter");
     JsonGeneratedCodecBuilder builder =
         new JsonGeneratedCodecBuilder(generatedPackage, className, type);
     AnyInfo any = codec.anyInfo();
     String code =
         any == null || any.writeField() == null && any.writeGetter() == null
-            ? new StringWriterCodegen(this)
-                .genWriterCode(builder, type, codec.writeFields(), 
objectMembers)
+            ? new StringWriterCodegen(this).genWriterCode(builder, type, 
codec.writeFields())
             : new StringWriterCodegen(this)
-                .genAnyWriterCode(builder, type, codec.writeFields(), any, 
objectMembers);
+                .genAnyWriterCode(builder, type, codec.writeFields(), any);
     return compileCodecClass(generatedPackage, className, code);
   }
 
   private Class<?> buildUtf8Writer(ObjectCodec<?> codec) {
-    return buildUtf8Writer(codec, false);
-  }
-
-  private Class<?> buildUtf8Writer(ObjectCodec<?> codec, boolean 
objectMembers) {
     Class<?> type = codec.type();
     String generatedPackage = CodeGenerator.getPackage(type);
-    String className = className(type, objectMembers ? "Utf8ObjectWriter" : 
"Utf8Writer");
+    String className = className(type, "Utf8Writer");
     JsonGeneratedCodecBuilder builder =
         new JsonGeneratedCodecBuilder(generatedPackage, className, type);
     AnyInfo any = codec.anyInfo();
     String code =
         any == null || any.writeField() == null && any.writeGetter() == null
-            ? new Utf8WriterCodegen(this)
-                .genWriterCode(builder, type, codec.writeFields(), 
objectMembers)
-            : new Utf8WriterCodegen(this)
-                .genAnyWriterCode(builder, type, codec.writeFields(), any, 
objectMembers);
+            ? new Utf8WriterCodegen(this).genWriterCode(builder, type, 
codec.writeFields())
+            : new Utf8WriterCodegen(this).genAnyWriterCode(builder, type, 
codec.writeFields(), any);
     return compileCodecClass(generatedPackage, className, code);
   }
 
diff --git 
a/java/fory-json/src/main/java/org/apache/fory/json/codegen/JsonWriterCodegen.java
 
b/java/fory-json/src/main/java/org/apache/fory/json/codegen/JsonWriterCodegen.java
index 6e0db4742..f13774caa 100644
--- 
a/java/fory-json/src/main/java/org/apache/fory/json/codegen/JsonWriterCodegen.java
+++ 
b/java/fory-json/src/main/java/org/apache/fory/json/codegen/JsonWriterCodegen.java
@@ -68,13 +68,13 @@ abstract class JsonWriterCodegen {
 
   abstract Class<?> codecArrayType();
 
-  abstract Class<?> objectWriterType();
-
   abstract Class<?> completeWriterType();
 
   abstract String writeMethod();
 
-  abstract String membersMethod();
+  // This names private split methods in ordinary complete writers. It is not 
a partial-object
+  // capability; keep the generated literal stable so unaffected writer source 
remains identical.
+  abstract String memberGroupMethod();
 
   abstract String writeAnyMethod();
 
@@ -173,16 +173,11 @@ abstract class JsonWriterCodegen {
   }
 
   String genWriterCode(
-      JsonGeneratedCodecBuilder builder,
-      Class<?> type,
-      JsonFieldInfo[] properties,
-      boolean objectMembers) {
+      JsonGeneratedCodecBuilder builder, Class<?> type, JsonFieldInfo[] 
properties) {
     ownerType = type;
     CodegenContext ctx = builder.context();
     ctx.addImports(writerType());
-    ctx.implementsInterfaces(
-        JsonCodegen.generatedCodecType(
-            ctx, objectMembers ? objectWriterType() : completeWriterType()));
+    ctx.implementsInterfaces(JsonCodegen.generatedCodecType(ctx, 
completeWriterType()));
     boolean objectStartFused = canFuseObjectStart(properties);
     PrefixFields prefixFields = prefixFields(properties, objectStartFused);
     for (int i = 0; i < properties.length; i++) {
@@ -243,45 +238,15 @@ abstract class JsonWriterCodegen {
         "writer",
         Object.class,
         "value");
-    if (objectMembers) {
-      ctx.clearExprState();
-      Expression memberObject =
-          inline(
-              new Expression.Cast(
-                  new Reference("value", TypeRef.of(Object.class)), 
TypeRef.of(type)));
-      Expression memberValue =
-          properties.length <= 1 ? memberObject : new 
Expression.Variable("object", memberObject);
-      Code.ExprCode membersBody =
-          writeMembersExpression(builder, properties, 
memberValue).genCode(ctx);
-      String membersCode = membersBody.code();
-      membersCode = membersCode == null ? "" : 
ctx.optimizeMethodCode(membersCode);
-      ctx.addMethod(
-          "@Override public final",
-          membersMethod(),
-          membersCode,
-          void.class,
-          writerType(),
-          "writer",
-          Object.class,
-          "value",
-          int.class,
-          "written");
-    }
     return ctx.genCode();
   }
 
   String genAnyWriterCode(
-      JsonGeneratedCodecBuilder builder,
-      Class<?> type,
-      JsonFieldInfo[] properties,
-      AnyInfo any,
-      boolean objectMembers) {
+      JsonGeneratedCodecBuilder builder, Class<?> type, JsonFieldInfo[] 
properties, AnyInfo any) {
     ownerType = type;
     CodegenContext ctx = builder.context();
     ctx.addImports(writerType(), ObjectCodec.class, Map.class);
-    ctx.implementsInterfaces(
-        JsonCodegen.generatedCodecType(
-            ctx, objectMembers ? objectWriterType() : completeWriterType()));
+    ctx.implementsInterfaces(JsonCodegen.generatedCodecType(ctx, 
completeWriterType()));
     boolean objectStartFused = any.writeIndex() > 0 && 
canFuseObjectStart(properties);
     PrefixFields prefixFields = prefixFields(properties, objectStartFused);
     addWriterFields(ctx, properties, prefixFields);
@@ -357,38 +322,6 @@ abstract class JsonWriterCodegen {
         Object.class,
         "value");
 
-    if (objectMembers) {
-      String anyMembersMethod = membersMethod() + "Any";
-      addGeneratedMethod(
-          ctx,
-          "private final",
-          anyMembersMethod,
-          writeAnyMembersExpression(
-              builder,
-              properties,
-              any,
-              new Reference("object", TypeRef.of(type)),
-              new Reference("written", TypeRef.of(int.class))),
-          void.class,
-          writerType(),
-          "writer",
-          type,
-          "object",
-          int.class,
-          "written");
-      String typeName = ctx.type(type);
-      ctx.addMethod(
-          "@Override public final",
-          membersMethod(),
-          "this." + anyMembersMethod + "(writer, (" + typeName + ") value, 
written);",
-          void.class,
-          writerType(),
-          "writer",
-          Object.class,
-          "value",
-          int.class,
-          "written");
-    }
     return ctx.genCode();
   }
 
@@ -451,31 +384,6 @@ abstract class JsonWriterCodegen {
     return !any.valueTypeInfo().usesDefaultObjectCodec() || any.valueRawType() 
!= ownerType;
   }
 
-  private Expression writeMembersExpression(
-      JsonGeneratedCodecBuilder builder, JsonFieldInfo[] properties, 
Expression object) {
-    Expression.ListExpression expressions = new Expression.ListExpression();
-    expressions.add(object);
-    Expression index = new Reference("written", TypeRef.of(int.class));
-    Reference writer = writerRef();
-    boolean splitMembers = properties.length >= splitMemberThreshold();
-    List<Expression> group = splitMembers ? new 
ArrayList<>(MAX_MEMBERS_PER_METHOD) : null;
-    for (int i = 0; i < properties.length; i++) {
-      Expression member = writeProp(builder, properties[i], i, true, index, 
object, writer);
-      if (splitMembers) {
-        group.add(member);
-        if (group.size() == MAX_MEMBERS_PER_METHOD) {
-          addMemberGroup(builder, expressions, group, object, writer);
-        }
-      } else {
-        expressions.add(member);
-      }
-    }
-    if (splitMembers) {
-      addMemberGroup(builder, expressions, group, object, writer, true);
-    }
-    return expressions;
-  }
-
   static final class PrefixFields {
     final boolean[] name;
     final boolean[] comma;
@@ -685,42 +593,6 @@ abstract class JsonWriterCodegen {
     return expressions;
   }
 
-  private Expression writeAnyMembersExpression(
-      JsonGeneratedCodecBuilder builder,
-      JsonFieldInfo[] properties,
-      AnyInfo any,
-      Expression object,
-      Expression written) {
-    Expression.ListExpression expressions = new 
Expression.ListExpression(object);
-    Reference writer = writerRef();
-    List<Expression> memberGroup =
-        properties.length >= splitMemberThreshold()
-            ? new ArrayList<>(MAX_MEMBERS_PER_METHOD)
-            : null;
-    for (int i = 0; i <= properties.length; i++) {
-      if (i == any.writeIndex()) {
-        flushAnyMemberGroup(builder, expressions, memberGroup, object, writer);
-        expressions.add(new Expression.Assign(written, writeAny(builder, any, 
object, written)));
-      }
-      if (i == properties.length) {
-        break;
-      }
-      Expression member = writeProp(builder, properties[i], i, true, written, 
object, writer);
-      if (memberGroup == null) {
-        expressions.add(member);
-      } else {
-        memberGroup.add(member);
-        if (memberGroup.size() == MAX_MEMBERS_PER_METHOD) {
-          addMemberGroup(builder, expressions, memberGroup, object, writer);
-        }
-      }
-    }
-    if (memberGroup != null) {
-      addMemberGroup(builder, expressions, memberGroup, object, writer, true);
-    }
-    return expressions;
-  }
-
   private void flushAnyMemberGroup(
       JsonGeneratedCodecBuilder builder,
       Expression.ListExpression expressions,
@@ -799,7 +671,7 @@ abstract class JsonWriterCodegen {
             builder.context(),
             cutPoints,
             new Expression.ListExpression(new ArrayList<>(memberGroup)),
-            membersMethod(),
+            memberGroupMethod(),
             false));
     memberGroup.clear();
   }
diff --git 
a/java/fory-json/src/main/java/org/apache/fory/json/codegen/StringWriterCodegen.java
 
b/java/fory-json/src/main/java/org/apache/fory/json/codegen/StringWriterCodegen.java
index cb90bdd24..732c8d943 100644
--- 
a/java/fory-json/src/main/java/org/apache/fory/json/codegen/StringWriterCodegen.java
+++ 
b/java/fory-json/src/main/java/org/apache/fory/json/codegen/StringWriterCodegen.java
@@ -23,7 +23,6 @@ import org.apache.fory.codegen.CodegenContext;
 import org.apache.fory.codegen.Expression;
 import org.apache.fory.codegen.Expression.Reference;
 import org.apache.fory.json.ForyJsonException;
-import org.apache.fory.json.codec.StringObjectWriter;
 import org.apache.fory.json.codec.StringWriterCodec;
 import org.apache.fory.json.meta.JsonFieldInfo;
 import org.apache.fory.json.writer.StringJsonWriter;
@@ -51,11 +50,6 @@ final class StringWriterCodegen extends JsonWriterCodegen {
     return StringWriterCodec[].class;
   }
 
-  @Override
-  Class<?> objectWriterType() {
-    return StringObjectWriter.class;
-  }
-
   @Override
   Class<?> completeWriterType() {
     return StringWriterCodec.class;
@@ -67,7 +61,7 @@ final class StringWriterCodegen extends JsonWriterCodegen {
   }
 
   @Override
-  String membersMethod() {
+  String memberGroupMethod() {
     return "writeStringMembers";
   }
 
diff --git 
a/java/fory-json/src/main/java/org/apache/fory/json/codegen/Utf8WriterCodegen.java
 
b/java/fory-json/src/main/java/org/apache/fory/json/codegen/Utf8WriterCodegen.java
index b99cfaf30..a8aa2e4b3 100644
--- 
a/java/fory-json/src/main/java/org/apache/fory/json/codegen/Utf8WriterCodegen.java
+++ 
b/java/fory-json/src/main/java/org/apache/fory/json/codegen/Utf8WriterCodegen.java
@@ -31,7 +31,6 @@ import org.apache.fory.codegen.Expression.Reference;
 import org.apache.fory.json.ForyJsonException;
 import org.apache.fory.json.codec.ArrayCodec;
 import org.apache.fory.json.codec.ScalarCodecs;
-import org.apache.fory.json.codec.Utf8ObjectWriter;
 import org.apache.fory.json.codec.Utf8WriterCodec;
 import org.apache.fory.json.meta.JsonFieldInfo;
 import org.apache.fory.json.meta.JsonFieldKind;
@@ -60,11 +59,6 @@ final class Utf8WriterCodegen extends JsonWriterCodegen {
     return Utf8WriterCodec[].class;
   }
 
-  @Override
-  Class<?> objectWriterType() {
-    return Utf8ObjectWriter.class;
-  }
-
   @Override
   Class<?> completeWriterType() {
     return Utf8WriterCodec.class;
@@ -76,7 +70,7 @@ final class Utf8WriterCodegen extends JsonWriterCodegen {
   }
 
   @Override
-  String membersMethod() {
+  String memberGroupMethod() {
     return "writeUtf8Members";
   }
 
diff --git 
a/java/fory-json/src/main/java/org/apache/fory/json/resolver/JsonTypeInfo.java 
b/java/fory-json/src/main/java/org/apache/fory/json/resolver/JsonTypeInfo.java
index c9fcef692..27055f503 100644
--- 
a/java/fory-json/src/main/java/org/apache/fory/json/resolver/JsonTypeInfo.java
+++ 
b/java/fory-json/src/main/java/org/apache/fory/json/resolver/JsonTypeInfo.java
@@ -40,9 +40,8 @@ import org.apache.fory.json.meta.JsonFieldKind;
  *
  * <p>Only the canonical exact raw-class {@link ObjectCodec} binding may 
receive generated
  * replacements. Custom codecs, parameterized object bindings, containers, 
scalars, and dynamic
- * {@code Object} bindings retain their original semantic owner. One generated 
class may implement
- * more than one capability when its code is genuinely shared, but 
installation still occurs per
- * slot and each slot is independently lazy.
+ * {@code Object} bindings retain their original semantic owner. Each complete 
capability is
+ * installed in its own independently lazy slot.
  */
 public final class JsonTypeInfo {
   private final Type type;
diff --git 
a/java/fory-json/src/main/java/org/apache/fory/json/resolver/JsonTypeResolver.java
 
b/java/fory-json/src/main/java/org/apache/fory/json/resolver/JsonTypeResolver.java
index 41668ed69..13730e264 100644
--- 
a/java/fory-json/src/main/java/org/apache/fory/json/resolver/JsonTypeResolver.java
+++ 
b/java/fory-json/src/main/java/org/apache/fory/json/resolver/JsonTypeResolver.java
@@ -35,10 +35,8 @@ import org.apache.fory.json.codec.JsonSubTypesInfo;
 import org.apache.fory.json.codec.Latin1ReaderCodec;
 import org.apache.fory.json.codec.ObjectCodec;
 import org.apache.fory.json.codec.ObjectCodec.AnyInfo;
-import org.apache.fory.json.codec.StringObjectWriter;
 import org.apache.fory.json.codec.StringWriterCodec;
 import org.apache.fory.json.codec.Utf16ReaderCodec;
-import org.apache.fory.json.codec.Utf8ObjectWriter;
 import org.apache.fory.json.codec.Utf8ReaderCodec;
 import org.apache.fory.json.codec.Utf8WriterCodec;
 import org.apache.fory.json.codegen.JsonCodegen;
@@ -261,86 +259,6 @@ public final class JsonTypeResolver {
     return (Utf8WriterCodec<T>) installed;
   }
 
-  /**
-   * Returns the current String member writer and requests JIT refinement when 
available.
-   *
-   * <p>The caller must hold this resolver's JIT lock.
-   */
-  @Internal
-  @SuppressWarnings("unchecked")
-  public <T> StringObjectWriter<T> stringObjectWriter(ObjectCodec<T> codec) {
-    requireJITLock();
-    ObjectCodec<Object> owner = erase(codec);
-    JsonTypeInfo typeInfo = objectTypeInfos.get(owner.type());
-    if (typeInfo == null) {
-      return codec;
-    }
-    StringWriterCodec<Object> installed = typeInfo.stringWriter();
-    if ((installed == owner || !(installed instanceof StringObjectWriter))
-        && codegen != null
-        && codegen.canCompileWriter(owner)) {
-      jitContext.registerJITCallback(
-          () -> owner.getClass(),
-          () -> codegen.compileStringObjectWriter(owner),
-          new JsonJITContext.JITCallback<Class<?>>() {
-            @Override
-            public void onSuccess(Class<?> generated) {
-              publishStringWriter(owner, typeInfo, generated);
-            }
-
-            @Override
-            public void onFailure(Throwable failure) {}
-
-            @Override
-            public Object id() {
-              return codegen.stringObjectWriterJITId(owner.type());
-            }
-          });
-      installed = typeInfo.stringWriter();
-    }
-    return installed instanceof StringObjectWriter ? (StringObjectWriter<T>) 
installed : codec;
-  }
-
-  /**
-   * Returns the current UTF-8 member writer and requests JIT refinement when 
available.
-   *
-   * <p>The caller must hold this resolver's JIT lock.
-   */
-  @Internal
-  @SuppressWarnings("unchecked")
-  public <T> Utf8ObjectWriter<T> utf8ObjectWriter(ObjectCodec<T> codec) {
-    requireJITLock();
-    ObjectCodec<Object> owner = erase(codec);
-    JsonTypeInfo typeInfo = objectTypeInfos.get(owner.type());
-    if (typeInfo == null) {
-      return codec;
-    }
-    Utf8WriterCodec<Object> installed = typeInfo.utf8Writer();
-    if ((installed == owner || !(installed instanceof Utf8ObjectWriter))
-        && codegen != null
-        && codegen.canCompileWriter(owner)) {
-      jitContext.registerJITCallback(
-          () -> owner.getClass(),
-          () -> codegen.compileUtf8ObjectWriter(owner),
-          new JsonJITContext.JITCallback<Class<?>>() {
-            @Override
-            public void onSuccess(Class<?> generated) {
-              publishUtf8Writer(owner, typeInfo, generated);
-            }
-
-            @Override
-            public void onFailure(Throwable failure) {}
-
-            @Override
-            public Object id() {
-              return codegen.utf8ObjectWriterJITId(owner.type());
-            }
-          });
-      installed = typeInfo.utf8Writer();
-    }
-    return installed instanceof Utf8ObjectWriter ? (Utf8ObjectWriter<T>) 
installed : codec;
-  }
-
   @SuppressWarnings("unchecked")
   public <T> Latin1ReaderCodec<T> latin1Reader(ObjectCodec<T> codec) {
     requireJITLock();
@@ -931,17 +849,6 @@ public final class JsonTypeResolver {
   private void publishStringWriter(
       ObjectCodec<Object> owner, JsonTypeInfo typeInfo, Class<?> generated) {
     requireJITLock();
-    StringWriterCodec<Object> current = typeInfo.stringWriter();
-    // A member writer is a parent-independent child refinement and is also a 
complete writer, so
-    // the canonical child slot can serve ordinary and composed writes. A 
concurrently compiled
-    // complete-only writer must never downgrade that refinement when its 
callback finishes later.
-    // The interpreted owner also implements member writing, so identity 
distinguishes it from an
-    // installed generated refinement.
-    if (!StringObjectWriter.class.isAssignableFrom(generated)
-        && current != owner
-        && current instanceof StringObjectWriter) {
-      return;
-    }
     StringWriterCodec<Object> codec = newStringWriter(owner, generated);
     Field[] childFields = writerChildFields(codec, owner);
     registerStringWriterCallbacks(codec, owner, childFields);
@@ -952,12 +859,6 @@ public final class JsonTypeResolver {
   private void publishUtf8Writer(
       ObjectCodec<Object> owner, JsonTypeInfo typeInfo, Class<?> generated) {
     requireJITLock();
-    Utf8WriterCodec<Object> current = typeInfo.utf8Writer();
-    if (!Utf8ObjectWriter.class.isAssignableFrom(generated)
-        && current != owner
-        && current instanceof Utf8ObjectWriter) {
-      return;
-    }
     Utf8WriterCodec<Object> codec = newUtf8Writer(owner, generated);
     Field[] childFields = writerChildFields(codec, owner);
     registerUtf8WriterCallbacks(codec, owner, childFields);
@@ -1015,7 +916,7 @@ public final class JsonTypeResolver {
               @Override
               public void onNotifyResult(Object result) {
                 StringWriterCodec<Object> codec = child.stringWriter();
-                checkGeneratedWriter(result, codec, StringObjectWriter.class);
+                checkGeneratedClass(result, codec);
                 ReflectionUtils.setObjectFieldValue(parent, field, codec);
               }
 
@@ -1044,7 +945,7 @@ public final class JsonTypeResolver {
               @Override
               public void onNotifyResult(Object result) {
                 Utf8WriterCodec<Object> codec = child.utf8Writer();
-                checkGeneratedWriter(result, codec, Utf8ObjectWriter.class);
+                checkGeneratedClass(result, codec);
                 ReflectionUtils.setObjectFieldValue(parent, field, codec);
               }
 
@@ -1155,7 +1056,7 @@ public final class JsonTypeResolver {
           @Override
           public void onNotifyResult(Object result) {
             StringWriterCodec<Object> codec = child.stringWriter();
-            checkGeneratedWriter(result, codec, StringObjectWriter.class);
+            checkGeneratedClass(result, codec);
             ReflectionUtils.setObjectFieldValue(parent, field, codec);
           }
 
@@ -1179,7 +1080,7 @@ public final class JsonTypeResolver {
           @Override
           public void onNotifyResult(Object result) {
             Utf8WriterCodec<Object> codec = child.utf8Writer();
-            checkGeneratedWriter(result, codec, Utf8ObjectWriter.class);
+            checkGeneratedClass(result, codec);
             ReflectionUtils.setObjectFieldValue(parent, field, codec);
           }
 
@@ -1353,13 +1254,6 @@ public final class JsonTypeResolver {
     }
   }
 
-  private static void checkGeneratedWriter(Object result, Object codec, 
Class<?> refinement) {
-    if (codec.getClass() != result && !refinement.isInstance(codec)) {
-      throw new IllegalStateException(
-          "Generated JSON callback does not match installed writer 
capability");
-    }
-  }
-
   private static Object typeInfoKey(Type declaredType, Class<?> rawType) {
     return declaredType instanceof Class ? rawType : declaredType;
   }
diff --git 
a/java/fory-json/src/test/java/org/apache/fory/json/JsonAnyPropertyTest.java 
b/java/fory-json/src/test/java/org/apache/fory/json/JsonAnyPropertyTest.java
index f625a04c8..2bdfeb111 100644
--- a/java/fory-json/src/test/java/org/apache/fory/json/JsonAnyPropertyTest.java
+++ b/java/fory-json/src/test/java/org/apache/fory/json/JsonAnyPropertyTest.java
@@ -699,6 +699,11 @@ public class JsonAnyPropertyTest extends 
ForyJsonTestModels {
     ExtensibleCircle value = new ExtensibleCircle();
     value.radius = 2;
     value.properties.put("color", 3);
+    String concrete = "{\"radius\":2,\"color\":3}";
+    assertEquals(json.toJson(value, ExtensibleCircle.class), concrete);
+    assertEquals(
+        new String(json.toJsonBytes(value, ExtensibleCircle.class), 
StandardCharsets.UTF_8),
+        concrete);
     String expected = "{\"kind\":\"circle\",\"radius\":2,\"color\":3}";
     assertEquals(json.toJson(value, ExtensibleShape.class), expected);
     assertEquals(
@@ -726,7 +731,7 @@ public class JsonAnyPropertyTest extends ForyJsonTestModels 
{
     assertEquals(
         new String(json.toJsonBytes(value, ExtensibleShape.class), 
StandardCharsets.UTF_8),
         duplicate);
-    assertGeneratedCapabilities(json, ExtensibleCircle.class);
+    assertGeneratedReaders(json, ExtensibleCircle.class);
   }
 
   @Test
@@ -737,7 +742,7 @@ public class JsonAnyPropertyTest extends ForyJsonTestModels 
{
             json.fromJson("{\"x\":1,\"kind\":\"method\",\"y\":2}", 
ExtensibleShape.class);
     assertEquals(value.storage, linkedMap("x", 1, "y", 2));
     assertEquals(value.calls, 2);
-    assertGeneratedCapabilities(json, MethodCircle.class);
+    assertGeneratedReaders(json, MethodCircle.class);
   }
 
   @Test
@@ -748,7 +753,7 @@ public class JsonAnyPropertyTest extends ForyJsonTestModels 
{
             json.fromJson("{\"radius\":1,\"kind\":\"creator\",\"x\":2}", 
ExtensibleShape.class);
     assertEquals(value.radius, 1);
     assertEquals(value.properties, Collections.singletonMap("x", 2));
-    assertGeneratedCapabilities(json, CreatorCircle.class);
+    assertGeneratedReaders(json, CreatorCircle.class);
   }
 
   @Test
@@ -764,14 +769,14 @@ public class JsonAnyPropertyTest extends 
ForyJsonTestModels {
 
     SharedCircle direct = json.fromJson("{\"kind\":5,\"type\":6}", 
SharedCircle.class);
     assertEquals(direct.properties, linkedMap("kind", 5, "type", 6));
-    assertGeneratedCapabilities(json, SharedCircle.class);
+    assertGeneratedReaders(json, SharedCircle.class);
   }
 
   @Test
   public void inlineRecursiveRead() {
     ForyJson json = newJson();
     json.fromJson("{\"kind\":\"recursive\"}", RecursiveShape.class);
-    assertGeneratedCapabilities(json, RecursiveCircle.class);
+    assertGeneratedReaders(json, RecursiveCircle.class);
 
     RecursiveCircle latin1 =
         (RecursiveCircle)
@@ -933,6 +938,23 @@ public class JsonAnyPropertyTest extends 
ForyJsonTestModels {
     }
   }
 
+  private void assertGeneratedReaders(ForyJson json, Class<?> type) {
+    JsonTypeResolver resolver = JsonTestSupport.primaryTypeResolver(json);
+    resolver.lockJIT();
+    try {
+      ObjectCodec<?> owner = resolver.getObjectCodec(type);
+      JsonTypeInfo info = resolver.getTypeInfo(type, type);
+      if (!StringSerializer.isBytesBackedString()) {
+        resolver.latin1Reader(owner);
+      }
+      assertGenerated(info.latin1Reader(), owner);
+      assertGenerated(info.utf16Reader(), owner);
+      assertGenerated(info.utf8Reader(), owner);
+    } finally {
+      resolver.unlockJIT();
+    }
+  }
+
   private static void assertInterpretedCapabilities(ForyJson json, TypeRef<?> 
type) {
     JsonTypeResolver resolver = JsonTestSupport.primaryTypeResolver(json);
     resolver.lockJIT();
diff --git 
a/java/fory-json/src/test/java/org/apache/fory/json/JsonAsyncCompilationTest.java
 
b/java/fory-json/src/test/java/org/apache/fory/json/JsonAsyncCompilationTest.java
index 366e07ea4..9758328bc 100644
--- 
a/java/fory-json/src/test/java/org/apache/fory/json/JsonAsyncCompilationTest.java
+++ 
b/java/fory-json/src/test/java/org/apache/fory/json/JsonAsyncCompilationTest.java
@@ -56,7 +56,6 @@ import org.apache.fory.json.codec.ClosedSubtypeCodec;
 import org.apache.fory.json.codec.JsonCodec;
 import org.apache.fory.json.codec.Latin1ReaderCodec;
 import org.apache.fory.json.codec.ObjectCodec;
-import org.apache.fory.json.codec.StringObjectWriter;
 import org.apache.fory.json.codec.StringWriterCodec;
 import org.apache.fory.json.codec.Utf16ReaderCodec;
 import org.apache.fory.json.codec.Utf8ReaderCodec;
@@ -321,34 +320,7 @@ public class JsonAsyncCompilationTest {
   }
 
   @Test
-  public void subtypeWriterRefinementWins() throws Exception {
-    ControlledJson controlled = controlledJson();
-    JsonTypeResolver resolver = primaryTypeResolver(controlled.json);
-    ObjectCodec<AsyncCircle> owner;
-    JsonTypeInfo child;
-    resolver.lockJIT();
-    try {
-      resolver.getTypeInfo(AsyncShape.class, AsyncShape.class);
-      owner = resolver.getObjectCodec(AsyncCircle.class);
-      child = resolver.getTypeInfo(AsyncCircle.class, AsyncCircle.class);
-      assertSame(resolver.stringWriter(owner), owner);
-    } finally {
-      resolver.unlockJIT();
-    }
-
-    // The subtype table queues its member writers first and the ordinary 
complete writer last.
-    // Completing all tasks proves that the later ordinary callback cannot 
downgrade the slot.
-    assertEquals(controlled.executor.pendingTasks(), 3);
-    controlled.executor.runAll();
-    assertTrue(child.stringWriter() instanceof StringObjectWriter);
-    assertNotSame(child.stringWriter(), owner);
-    assertEquals(
-        controlled.json.toJson(new AsyncCircle(3), AsyncShape.class),
-        "{\"kind\":\"circle\",\"radius\":3}");
-  }
-
-  @Test
-  public void rolledBackSubtypeTasksStayIsolated() throws Exception {
+  public void rolledBackReaderTasksStayIsolated() throws Exception {
     ControlledJson controlled = controlledJson();
     JsonTypeResolver resolver = primaryTypeResolver(controlled.json);
     resolver.lockJIT();
@@ -358,21 +330,22 @@ public class JsonAsyncCompilationTest {
       expectThrows(
           ForyJsonException.class,
           () -> resolver.getTypeInfo(BrokenShape.class, BrokenShape.class));
-      assertEquals(controlled.executor.pendingTasks(), 2);
+      assertEquals(controlled.executor.pendingTasks(), 3);
       replacementOwner = resolver.getObjectCodec(RollbackCircle.class);
       replacement = resolver.getTypeInfo(RollbackCircle.class, 
RollbackCircle.class);
-      assertSame(replacement.stringWriter(), replacementOwner);
-      assertSame(replacement.utf8Writer(), replacementOwner);
+      assertSame(replacement.latin1Reader(), replacementOwner);
+      assertSame(replacement.utf16Reader(), replacementOwner);
+      assertSame(replacement.utf8Reader(), replacementOwner);
     } finally {
       resolver.unlockJIT();
     }
 
-    // The failed subtype transaction owned the queued member-writer requests. 
Their callbacks may
-    // finish, but they must target the removed slots captured by those 
requests rather than the
-    // replacement generation now registered for the same class.
+    // The failed subtype transaction queued parent-local Any readers against 
its provisional
+    // slots. Completing those tasks must not mutate the replacement 
generation for the same class.
     controlled.executor.runAll();
-    assertSame(replacement.stringWriter(), replacementOwner);
-    assertSame(replacement.utf8Writer(), replacementOwner);
+    assertSame(replacement.latin1Reader(), replacementOwner);
+    assertSame(replacement.utf16Reader(), replacementOwner);
+    assertSame(replacement.utf8Reader(), replacementOwner);
   }
 
   @Test
@@ -1096,21 +1069,6 @@ public class JsonAsyncCompilationTest {
     return child;
   }
 
-  @JsonSubTypes(
-      property = "kind",
-      value = {@JsonSubTypes.Type(value = AsyncCircle.class, name = "circle")})
-  public interface AsyncShape {}
-
-  public static final class AsyncCircle implements AsyncShape {
-    public int radius;
-
-    public AsyncCircle() {}
-
-    private AsyncCircle(int radius) {
-      this.radius = radius;
-    }
-  }
-
   @JsonSubTypes(
       property = "kind",
       value = {@JsonSubTypes.Type(value = AsyncInlineChild.class, name = 
"child")})
@@ -1129,7 +1087,7 @@ public class JsonAsyncCompilationTest {
   public interface BrokenShape {}
 
   public static final class RollbackCircle implements BrokenShape {
-    public int radius;
+    @JsonAnyProperty public Map<String, Integer> properties;
   }
 
   public static final class CollidingSubtype implements BrokenShape {
diff --git 
a/java/fory-json/src/test/java/org/apache/fory/json/JsonSubTypesTest.java 
b/java/fory-json/src/test/java/org/apache/fory/json/JsonSubTypesTest.java
index 99713359e..89ecd04ba 100644
--- a/java/fory-json/src/test/java/org/apache/fory/json/JsonSubTypesTest.java
+++ b/java/fory-json/src/test/java/org/apache/fory/json/JsonSubTypesTest.java
@@ -23,8 +23,6 @@ import static 
org.apache.fory.json.JsonTestSupport.newLatin1Reader;
 import static org.apache.fory.json.JsonTestSupport.newUtf16Reader;
 import static org.apache.fory.json.JsonTestSupport.newUtf8Reader;
 import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertNotSame;
-import static org.testng.Assert.assertSame;
 import static org.testng.Assert.assertThrows;
 
 import java.io.ByteArrayOutputStream;
@@ -35,14 +33,11 @@ import org.apache.fory.json.annotation.JsonPropertyOrder;
 import org.apache.fory.json.annotation.JsonSubTypes;
 import org.apache.fory.json.annotation.JsonSubTypes.Inclusion;
 import org.apache.fory.json.codec.JsonCodec;
-import org.apache.fory.json.codec.ObjectCodec;
 import org.apache.fory.json.meta.JsonSubtypeScanInfo;
 import org.apache.fory.json.reader.JsonReader;
 import org.apache.fory.json.reader.Latin1JsonReader;
 import org.apache.fory.json.reader.Utf16JsonReader;
 import org.apache.fory.json.reader.Utf8JsonReader;
-import org.apache.fory.json.resolver.JsonTypeInfo;
-import org.apache.fory.json.resolver.JsonTypeResolver;
 import org.apache.fory.json.writer.StringJsonWriter;
 import org.apache.fory.json.writer.Utf8JsonWriter;
 import org.apache.fory.reflect.TypeRef;
@@ -58,45 +53,33 @@ public class JsonSubTypesTest extends ForyJsonTestModels {
   @Test
   public void inlineTypedRoot() {
     ForyJson json = newJson();
-    Shape value = new Circle(2);
+    Circle value = new Circle(2);
+    assertEquals(json.toJson(value, Circle.class), "{\"radius\":2}");
+    assertEquals(
+        new String(json.toJsonBytes(value, Circle.class), 
StandardCharsets.UTF_8),
+        "{\"radius\":2}");
     String text = json.toJson(value, Shape.class);
     assertEquals(text, "{\"kind\":\"circle\",\"radius\":2}");
     Shape decoded = json.fromJson(text, Shape.class);
     assertEquals(((Circle) decoded).radius, 2);
     assertEquals(new String(json.toJsonBytes(value, Shape.class), 
StandardCharsets.UTF_8), text);
-    assertInlineWriterCapabilities(json, Circle.class);
     Shape utf16 = 
json.fromJson("{\"说明\":\"值\",\"radius\":3,\"kind\":\"circle\"}", Shape.class);
     assertEquals(((Circle) utf16).radius, 3);
     Shape utf8 = json.fromJson(text.getBytes(StandardCharsets.UTF_8), 
Shape.class);
     assertEquals(((Circle) utf8).radius, 2);
   }
 
-  private void assertInlineWriterCapabilities(ForyJson json, Class<?> type) {
-    JsonTypeResolver resolver = JsonTestSupport.primaryTypeResolver(json);
-    resolver.lockJIT();
-    try {
-      JsonTypeInfo info = resolver.getTypeInfo(type, type);
-      ObjectCodec<?> owner = resolver.getObjectCodec(type);
-      if (codegenEnabled()) {
-        assertNotSame(info.stringWriter(), owner);
-        assertNotSame(info.utf8Writer(), owner);
-      } else {
-        assertSame(info.stringWriter(), owner);
-        assertSame(info.utf8Writer(), owner);
-      }
-    } finally {
-      resolver.unlockJIT();
-    }
-  }
-
   @Test
   public void classNameSubtype() {
     ForyJson json = newJson();
-    Shape value = new Rectangle(3, 4);
+    Rectangle value = new Rectangle(3, 4);
+    assertEquals(json.toJson(value, Rectangle.class), 
"{\"height\":4,\"width\":3}");
+    assertEquals(
+        new String(json.toJsonBytes(value, Rectangle.class), 
StandardCharsets.UTF_8),
+        "{\"height\":4,\"width\":3}");
     String text = json.toJson(value, Shape.class);
     assertEquals(text, "{\"kind\":\"rectangle\",\"height\":4,\"width\":3}");
     assertEquals(new String(json.toJsonBytes(value, Shape.class), 
StandardCharsets.UTF_8), text);
-    assertInlineWriterCapabilities(json, Rectangle.class);
     Shape decoded = json.fromJson(text, Shape.class);
     assertEquals(((Rectangle) decoded).width, 3);
     assertEquals(((Rectangle) decoded).height, 4);


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to