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-site.git
The following commit(s) were added to refs/heads/main by this push:
new 02c63cc79e 🔄 synced local 'docs/guide/' with remote 'docs/guide/'
02c63cc79e is described below
commit 02c63cc79e4a8ab548553c10504c5583ed80357c
Author: chaokunyang <[email protected]>
AuthorDate: Tue Jul 14 11:12:18 2026 +0000
🔄 synced local 'docs/guide/' with remote 'docs/guide/'
---
docs/guide/java/json-support.md | 127 +++++++++++++++++++++++++++++++++++++---
1 file changed, 118 insertions(+), 9 deletions(-)
diff --git a/docs/guide/java/json-support.md b/docs/guide/java/json-support.md
index 6bb3038f7a..c41c7f9ee1 100644
--- a/docs/guide/java/json-support.md
+++ b/docs/guide/java/json-support.md
@@ -113,9 +113,10 @@ public final class JsonExample {
}
```
-Unknown input properties are skipped. Null object properties are omitted by
default. Default JSON
-member discovery order is not a compatibility contract; use
`JsonPropertyOrder` or
-`JsonProperty.index` when emitted member order must be explicit.
+Unknown input properties are skipped unless a read-enabled Any field or
any-setter receives them.
+Null object properties are omitted by default. Default JSON member discovery
order is not a
+compatibility contract; use `JsonPropertyOrder` or `JsonProperty.index` when
emitted member order
+must be explicit.
## Reading and writing
@@ -299,12 +300,17 @@ output size. Builder changes after `build()` do not
mutate an existing runtime.
## Annotations
-Fory JSON provides `JsonProperty`, `JsonPropertyOrder`, `JsonIgnore`,
`JsonCreator`, and
-`JsonSubTypes` under `org.apache.fory.json.annotation`. They are not Jackson,
Gson, or Fory
-binary-protocol annotations.
+Fory JSON provides `JsonProperty`, `JsonPropertyOrder`, `JsonIgnore`,
`JsonAnyProperty`,
+`JsonAnyGetter`, `JsonAnySetter`, `JsonCreator`, and `JsonSubTypes` under
+`org.apache.fory.json.annotation`. They are not Jackson, Gson, or Fory
binary-protocol annotations.
```java
+import java.util.LinkedHashMap;
+import java.util.Map;
import org.apache.fory.json.PropertyNamingStrategy;
+import org.apache.fory.json.annotation.JsonAnyGetter;
+import org.apache.fory.json.annotation.JsonAnyProperty;
+import org.apache.fory.json.annotation.JsonAnySetter;
import org.apache.fory.json.annotation.JsonCreator;
import org.apache.fory.json.annotation.JsonIgnore;
import org.apache.fory.json.annotation.JsonProperty;
@@ -340,7 +346,8 @@ setter-only, creator-only, or write-ignored property is
invalid.
Inclusion affects writing only. Identical repeated declarations are allowed;
conflicting explicit
names, indexes, or non-default policies fail. Two properties cannot normalize
to the same JSON
-name. `NON_EMPTY`, aliases, formatting, and annotation-selected codecs are
unsupported.
+name. `JsonProperty` cannot be combined with an Any logical property or
declared on a
+`JsonAnySetter`. `NON_EMPTY`, aliases, formatting, and annotation-selected
codecs are unsupported.
### `JsonPropertyOrder`
@@ -387,6 +394,26 @@ subclass properties. Interface declarations are not
considered.
Property order affects serialization only. Deserialization remains name-based
and accepts members
in any order. Subtype discriminators remain before user properties.
+A write-enabled `JsonAnyProperty` or `JsonAnyGetter` participates as one
position identified by its
+Java logical property name:
+
+```java
+@JsonPropertyOrder({"id", "properties", "timestamp"})
+public final class Event {
+ public String id;
+
+ @JsonAnyProperty
+ public Map<String, Object> properties;
+
+ public long timestamp;
+}
+```
+
+The position emits every `properties` entry in Map iteration order between
`id` and `timestamp`; it
+does not emit a member named `properties`. Naming strategies do not transform
the Any ordering
+name. Input-only Any fields and `JsonAnySetter` have no write position.
Dynamic keys cannot appear
+in `JsonPropertyOrder`, and alphabetic ordering never sorts entries inside the
Map.
+
### Naming strategy
```java
@@ -399,7 +426,7 @@ ForyJson json =
The default `LOWER_CAMEL_CASE` preserves the discovered Java logical property
name. `SNAKE_CASE`
maps `userName` to `user_name`, `URLValue` to `url_value`, and `version2FA` to
`version2_fa`.
Explicit `JsonProperty` names, parameter-local creator names, and subtype
discriminator properties
-bypass the strategy.
+bypass the strategy. Dynamic Any keys also bypass it.
### `JsonIgnore`
@@ -413,6 +440,87 @@ private String serverManagedValue;
Both flags default to true. Accessors cannot restore an ignored direction, and
`JsonProperty`
cannot override it. Fory core's `Expose` has no effect in Fory JSON.
+### Dynamic object members
+
+Use `JsonAnyProperty` to flatten a `Map<String, V>` field into the containing
JSON object and store
+otherwise unknown input members:
+
+```java
+public final class Event {
+ public String id;
+
+ @JsonAnyProperty
+ public Map<String, Object> properties = new LinkedHashMap<>();
+}
+```
+
+For `properties` containing `"source" -> "mobile"`, the result contains
`"source":"mobile"`
+beside `id`; no nested `properties` member is written. The field reads and
writes by default.
+`JsonIgnore` may select one direction, but it cannot disable both. During
reading, Fory reuses an
+existing Map or initializes a null non-final field on the first unknown
member. A readable final
+field on an ordinary mutable object must already contain a mutable Map.
Records and property-list
+`JsonCreator` types instead receive the accumulated Map through their
construction argument.
+
+Use `JsonAnyGetter` and `JsonAnySetter` for method-backed writing and reading:
+
+```java
+public final class Event {
+ private final Map<String, Object> properties = new LinkedHashMap<>();
+
+ @JsonAnyGetter
+ public Map<String, Object> getProperties() {
+ return properties;
+ }
+
+ @JsonAnySetter
+ public void putProperty(String name, Object value) {
+ properties.put(name, value);
+ }
+}
+```
+
+An any-getter must be a public instance method with no arguments and a
`Map<String, V>` return type.
+An any-setter must be a public instance method with signature `void
method(String, V)`. Either may
+be used independently. When paired, their resolved value types must match
after primitive types are
+boxed. A primitive any-setter value rejects JSON null. Any-setters are not
supported on records or
+types using `JsonCreator`.
+
+A read-enabled `JsonAnyProperty` on a record component supplies that component
from unknown input.
+In property-list creator mode, a read-enabled Any field must correspond to one
listed creator
+argument; parameter-local creator mode cannot bind it. A write-only Any field
or any-getter cannot
+occupy a creator argument. If one claims a record component, that component
receives its normal
+Java default during reading.
+
+An any-getter claims its complete Java logical property: both
`getProperties()` and `properties()`
+claim `properties`, so same-named ordinary fields and accessors are not mapped
again as a fixed
+member. Fory does not infer a differently named backing field; use
`JsonIgnore` if that field must
+not be mapped separately. An any-setter has no logical property name and does
not claim a field.
+
+The logical name is used only for grouping and `JsonPropertyOrder`; it is not
a fixed JSON member.
+An input member with that name is a dynamic entry rather than a nested
aggregate. The same output
+key remains valid unless another fixed property conflicts with it.
+
+One effective type hierarchy may use either one Any field or up to one
any-getter and one
+any-setter. Field-backed and method-backed forms cannot be mixed, and method
annotations are invalid
+in field mode. An unannotated override disables an inherited method
annotation. `JsonProperty` is
+invalid on an any-setter and on every member claimed by an Any field or
getter. A same-named field
+cannot use `JsonIgnore` to suppress an any-getter's write direction, and its
`ignoreRead` flag does
+not disable a separate any-setter.
+
+Dynamic keys are emitted unchanged in Map iteration order. A null Map emits
nothing, while a null
+Map value emits JSON null regardless of fixed-property null settings. Null and
non-String output
+keys are rejected. Raw Maps, wildcard or unresolved keys, and non-String key
types are invalid.
+Declared fixed members, including members excluded from reading, are not
delivered to an Any
+input. Output keys whose Fory field-name hash conflicts with a fixed property
are rejected,
+including differently spelled hash collisions. Fory does not inspect an Any
Map for a key whose
+name or Fory field-name hash conflicts with an inline subtype discriminator.
An exact-name output
+key emits a duplicate JSON member; on input, a differently spelled hash
collision is classified as
+the discriminator by the child field table. Applications must keep dynamic
keys distinct from the
+active discriminator by both name and hash. Repeated unknown names replace the
Map value; an
+any-setter is called for every occurrence. Fixed input lookup is also
hash-based, so a differently
+spelled colliding name follows the fixed member instead of Any handling.
Escaped input names are
+decoded before delivery.
+
### `JsonCreator`
The compact mode lists existing Java logical property names in parameter order
and reuses their
@@ -643,7 +751,8 @@ Circular graphs eventually fail `maxDepth`; they are not
reconstructed.
| Declared write fails | Remove wildcard/type variables and pass
an assignable value; primitive declarations reject null |
| Immutable value is empty | Use a record, valid creator, or custom
codec |
| Ordinary object cannot be created | Add a usable no-argument constructor,
use a record or creator, or register a codec |
-| Accessor annotation fails | Use an eligible public JavaBean
accessor and disable field mode |
+| Ordinary accessor annotation fails | Use an eligible public JavaBean
accessor and disable field mode |
+| Any annotation fails | Use one field form or one valid method
pair with resolved `Map<String, V>` types |
| Subtype fails | Write with the declared base, list the
exact runtime class, and use the configured wire shape |
| Collection fails | Target a supported interface/common
implementation or register a codec |
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]