Decode#

Static methods that turn JSON text, or a tree from Json.parse, into the typed records: ProjectData, SolveOptions, SchedulingChanges and the rest. Use it to load JSON into the typed API. The class has no instances. It keeps no state between calls, so any thread can call it.

Declaration#

package org.dhtmlx.plen;

public final class Decode {
  public static <T> T fromJson(String json, Class<T> type)
  public static <T> Vec<T> list(String json, Class<T> type)
  public static <T> T fromValue(Object value, Class<T> type)

  public static ProjectData project(Object value)
  public static ProjectOptions projectOptions(Object value)
  public static SolveOptions solveOptions(Object value)
  public static PreviewOptions previewOptions(Object value)
  public static AnalyzeOptions analyzeOptions(Object value)
  public static LevelingOptions levelingOptions(Object value)
  public static SchedulingChanges changes(Object value)
  public static EvaluateOptions evaluateOptions(Object value)
  public static LoadProfileOptions loadProfileOptions(Object value)
  public static SuggestOptions suggestOptions(Object value)
  public static Pool pool(Object value)
  public static Vec<Candidate> candidates(Object value)
  public static Vec<Slot> slots(Object value)
  public static Vec<Requirement> requirements(Object value)
}

Static methods#

fromJson#

public static <T> T fromJson(String json, Class<T> type)

Parses json and decodes the document into a new type instance.

Name Type Description
json String One JSON document.
type Class<T> The record class, such as ProjectData.class, SolveOptions.class or SchedulingChanges.class. Any class is accepted.

Returns - a new T built from the document.

Errors

Error When
InputException, kind PARSE Malformed JSON, a duplicate key, the document null, a value of the wrong JSON type, a missing or null required field, an unknown key on a strict record.
InputException, kind VALIDATION An invalid date string, an unknown enum value, level: true.
NullPointerException json is null.

InputException.path is null for every error Decode throws.

list#

public static <T> Vec<T> list(String json, Class<T> type)

Parses json as a JSON array and decodes each element into a type instance. Use it for top-level arrays: candidates, slots, requirements.

Name Type Description
json String A JSON array.
type Class<T> The element class, such as Candidate.class.

Returns - a new Vec<T> in array order.

Errors - InputException (PARSE) when the document isn't an array or an element is null, plus every error of fromJson.

fromValue#

public static <T> T fromValue(Object value, Class<T> type)

Decodes an already parsed tree: Map<String, Object>, List<Object>, String, Boolean, Number and null, as Json.parse returns them. Use it when the payload is part of a larger document parsed once.

Name Type Description
value Object The tree for one record. Nullable.
type Class<T> The record class.

Returns - a new T, or null when value is null.

Errors - the decoding errors of fromJson. There's no syntax check, because the tree is already parsed.

Typed shortcuts#

public static ProjectData project(Object value)
public static SolveOptions solveOptions(Object value)
public static Vec<Candidate> candidates(Object value)
// ... one per type in the declaration

Each record shortcut is fromValue(value, X.class) for its return type. candidates, slots and requirements take a List tree and decode each element, the same way as list.

Method Returns
project ProjectData
projectOptions ProjectOptions
solveOptions SolveOptions
previewOptions PreviewOptions
analyzeOptions AnalyzeOptions
levelingOptions LevelingOptions; null for false
changes SchedulingChanges
evaluateOptions EvaluateOptions
loadProfileOptions LoadProfileOptions
suggestOptions SuggestOptions
pool Pool
candidates Vec<Candidate>
slots Vec<Slot>
requirements Vec<Requirement>

Errors - the decoding errors of fromJson. The list shortcuts also throw InputException (PARSE) when value isn't a List or holds a null element.

Decoding rules#

These rules apply to every method above:

  • Keys. A snake_case key binds to the camelCase field (start_date → startDate, calendar_id → calendarId). type binds to taskType on task records and linkType on link records. On data records (ProjectData, TaskData, LinkData and the like) the camelCase key binds too, and unknown keys are ignored, so UI fields such as text pass through. Option, change-set and skill records (SolveOptions, SchedulingChanges, Changes, Candidate, Slot, Pool, Requirement and the like) accept only their listed keys. Any other key is a PARSE error that names the allowed keys.
  • Required fields. A required field (tasks on ProjectData, id on TaskData, source and target on links, ...) that's missing or null is a PARSE error.
  • null and missing. null on an ordinary field leaves the field's default. On a Patch field, null decodes to Patch.nullValue() (clear) and a missing key stays Patch.missing(). A null element inside a list, or a null map value, is a PARSE error.
  • Numbers. int and long fields need an integral number. int fields accept 0 to 4294967295 and cap values above Integer.MAX_VALUE at Integer.MAX_VALUE. Ids accept a number or a string.
  • Enums. Enum values are matched without regard to case, with - read as _ ("asap", "ASAP", "task-inherited"). Spellings that are specific to each enum are on its type page.
  • Value checks. Decoding checks only JSON types, required fields, dates and enum names. Range and cross-field checks such as priority in 0..1000 run later, when the record reaches a Project call.

Example#

import org.dhtmlx.plen.*;

public class DecodeProjectJson {
  public static void main(String[] args) throws Exception {
    String data = """
        {
          "tasks": [
            {"id": 1, "text": "Design", "start_date": "2026-03-02", "duration": 3},
            {"id": 2, "text": "Build", "start_date": "2026-03-02", "duration": 5}
          ],
          "links": [{"id": 1, "source": 1, "target": 2, "type": "0"}]
        }
        """;

    ProjectData project = Decode.fromJson(data, ProjectData.class);
    SolveOptions options = Decode.fromJson("{\"includeUnchanged\": true}", SolveOptions.class);

    ScheduleResult result = new Project().solve(project, options);
    for (Mutation m : result.updates) {
      System.out.println(m.id + ": " + m.startDate + " - " + m.endDate);
    }
    // 1: 2026-03-02 00:00 - 2026-03-05 00:00
    // 2: 2026-03-05 00:00 - 2026-03-12 00:00

    try {
      Decode.fromJson("{\"includeUnchanged\": true, \"bogus\": 1}", SolveOptions.class);
    } catch (InputException e) {
      System.out.println(e.kind + ": " + e.getMessage());
      // PARSE: unknown field `bogus`, expected one of `subtree`, `analytics`, `reasons`, `includeUnchanged`
    }
  }
}

To return typed results as JSON, use Encode.text. For a full request and response round trip, see Exchange JSON with a client.