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]