Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions CodenameOne/src/com/codename1/annotations/JsonAdapter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Codename One designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Codename One through http://www.codenameone.com/ if you
* need additional information or have any questions.
*/
package com.codename1.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/// Hands a type's JSON form to the application's own [JsonTypeAdapter].
///
/// On a field it applies to that field; on a class, to every field, element
/// and body of that class, which is how a type the generated codecs cannot
/// map -- a money value, an identifier wrapper -- gets one form everywhere.
/// A field's adapter wins over its class's.
///
/// ```java
/// public final class EpochSecondsAdapter implements JsonTypeAdapter<java.util.Date> {
/// public Object toJson(java.util.Date d) {
/// return d == null ? null : Long.valueOf(d.getTime() / 1000);
/// }
/// public java.util.Date fromJson(Object json) {
/// if (json == null) {
/// return null;
/// }
/// // instanceof, never a bare cast: a failed cast does not throw on iOS.
/// if (!(json instanceof Number)) {
/// throw new IllegalArgumentException("expected epoch seconds");
/// }
/// return new java.util.Date(((Number) json).longValue() * 1000);
/// }
/// }
///
/// @Mapped
/// public class Invoice {
/// @JsonAdapter(EpochSecondsAdapter.class)
/// public java.util.Date issued;
/// }
/// ```
///
/// The adapter needs a public constructor without arguments. The generated code
/// creates one instance per adapter class and calls it directly, so nothing is
/// found by name at run time and the translated binary keeps the class.
@com.codename1.impl.SharedWithBackend
@Retention(RetentionPolicy.CLASS)
@Target({ElementType.TYPE, ElementType.FIELD})
public @interface JsonAdapter {
/// The adapter class.
Class<? extends JsonTypeAdapter> value();
}
63 changes: 63 additions & 0 deletions CodenameOne/src/com/codename1/annotations/JsonCreator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Codename One designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Codename One through http://www.codenameone.com/ if you
* need additional information or have any questions.
*/
package com.codename1.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/// The constructor, or static factory method, that builds an immutable class
/// from JSON.
///
/// A class with final fields and no constructor without arguments cannot be
/// filled in field by field, so its JSON is read into one local per member and
/// handed to this constructor at once -- as a record's canonical constructor
/// is. Every parameter carries [JsonProperty] naming the member it is read
/// from; a compiled class does not keep parameter names, so there is nothing
/// else to match them by. A member a body leaves out arrives as its type's
/// default (`null`, `0`, `false`) unless it is `required`.
///
/// ```java
/// @Mapped
/// public final class Point {
/// private final int x;
/// private final int y;
///
/// @JsonCreator
/// public Point(@JsonProperty("x") int x, @JsonProperty("y") int y) {
/// this.x = x;
/// this.y = y;
/// }
///
/// public int getX() { return x; }
/// public int getY() { return y; }
/// }
/// ```
///
/// The generated code calls it directly; nothing is looked up by name.
@com.codename1.impl.SharedWithBackend
@Retention(RetentionPolicy.CLASS)
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD})
public @interface JsonCreator {
}
58 changes: 58 additions & 0 deletions CodenameOne/src/com/codename1/annotations/JsonFormat.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Codename One designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Codename One through http://www.codenameone.com/ if you
* need additional information or have any questions.
*/
package com.codename1.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/// Picks the JSON shape a date or number field is written in.
///
/// - A `java.util.Date` is milliseconds since the epoch by default. With
/// [Shape#STRING] it is an ISO-8601 instant in UTC,
/// `2026-10-25T08:30:00.000Z`.
/// - A number is a JSON number by default. With [Shape#STRING] it is the same
/// digits in quotes -- what a JavaScript client needs for a `long` past 2^53,
/// which its numbers cannot hold exactly.
///
/// Reading accepts either shape whatever the annotation says, so a field can
/// change its format without breaking the clients that still send the old one.
/// Any other combination is refused by the build.
@com.codename1.impl.SharedWithBackend
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.FIELD)
public @interface JsonFormat {
/// The shape the value is written in.
Shape value();

/// A JSON shape for a scalar value.
enum Shape {
/// The type's own form: a number, or a date as epoch milliseconds.
DEFAULT,
/// A string: a number's digits, or a date in ISO-8601.
STRING,
/// A number: a date as epoch milliseconds.
NUMBER
}
}
39 changes: 39 additions & 0 deletions CodenameOne/src/com/codename1/annotations/JsonOmitNull.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Codename One designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Codename One through http://www.codenameone.com/ if you
* need additional information or have any questions.
*/
package com.codename1.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/// Leaves a member that is `null` out of the JSON instead of writing `null`.
///
/// On a field it applies to that field; on a class, to every field of it.
/// Reading is unaffected: an absent member leaves the field as the constructor
/// left it, and an explicit `null` sets it to `null`.
@com.codename1.impl.SharedWithBackend
@Retention(RetentionPolicy.CLASS)
@Target({ElementType.TYPE, ElementType.FIELD})
public @interface JsonOmitNull {
}
28 changes: 22 additions & 6 deletions CodenameOne/src/com/codename1/annotations/JsonProperty.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,32 @@
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/// Renames a `@Mapped` field in the JSON projection. The default JSON key is
/// the field name; `@JsonProperty` lets a field map to `snake_case` or any
/// alternative spelling without touching the Java identifier.
/// Names a field in the JSON form, and says how a body may spell it.
///
/// The default JSON key is the field name; `value` lets a field map to
/// `snake_case` or any other spelling without touching the Java identifier.
/// `aliases` are further names a body may use for the same member -- an old
/// spelling a client still sends -- and are only ever read: the value is
/// always written under `value`. `required` makes a body that leaves the
/// member out a refused body, naming the member; an explicit `null` is still
/// a value, so it is accepted (and refused for a primitive, which cannot hold it).
///
/// On a parameter of a [JsonCreator] constructor or factory it names the member
/// that argument is read from, since a compiled class does not keep its
/// parameter names.
///
/// The server's generated codecs read it too, so a class shared between an app
/// and its backend has one JSON form on both sides.
@com.codename1.impl.SharedWithBackend
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.FIELD)
@Target({ElementType.FIELD, ElementType.PARAMETER})
public @interface JsonProperty {
/// The JSON key.
String value();
/// The JSON key; empty for the field's own name.
String value() default "";

/// Other keys a body may carry the value under. Read only, never written.
String[] aliases() default {};

/// Whether a body that leaves the member out is refused.
boolean required() default false;
}
42 changes: 42 additions & 0 deletions CodenameOne/src/com/codename1/annotations/JsonStrict.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Codename One designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Codename One through http://www.codenameone.com/ if you
* need additional information or have any questions.
*/
package com.codename1.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/// Refuses a body that carries a member this class does not declare.
///
/// By default an unknown member is ignored, as a Spring Boot application
/// ignores one, so a newer client can talk to an older server. A contract that
/// would rather hear about a misspelt member than lose it silently marks the
/// class: the read then fails with the member's path, for example
/// `$.items[2].colour: unknown member`. Names a field lists in
/// [JsonProperty#aliases] are known; names of [JsonIgnore] fields are not.
@com.codename1.impl.SharedWithBackend
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.TYPE)
public @interface JsonStrict {
}
61 changes: 61 additions & 0 deletions CodenameOne/src/com/codename1/annotations/JsonTypeAdapter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Codename One designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Codename One through http://www.codenameone.com/ if you
* need additional information or have any questions.
*/
package com.codename1.annotations;

/// Converts a value to and from its JSON form, for a field or class marked
/// [JsonAdapter].
///
/// Both directions use the parsed form of JSON: `null`, a `String`, a
/// `Boolean`, a `Number`, a `java.util.Map` with `String` keys for an object
/// and a `java.util.List` for an array, nested as deeply as needed. A parser may
/// hand a number over as any `Number` subclass, so read one through
/// `Number#longValue()` or `Number#doubleValue()` rather than casting it to a
/// particular class.
///
/// Throw `IllegalArgumentException` from [#fromJson(Object)] to refuse a value;
/// the generated code adds where in the document it was. `T` is the type
/// converted.
@com.codename1.impl.SharedWithBackend
public interface JsonTypeAdapter<T> {
/// The JSON form of `value`, which may be `null`.
///
/// #### Parameters
///
/// - `value`: the value to write
///
/// #### Returns
///
/// its parsed JSON form
Object toJson(T value);

/// The value `json` is the form of; `json` may be `null`.
///
/// #### Parameters
///
/// - `json`: the parsed JSON value
///
/// #### Returns
///
/// the value it stands for
T fromJson(Object json);
}
Loading
Loading