==== Value API

The javadoc:value.Value[Value] is a unified, type-safe API for accessing all parameter types:

* Header
* Path
* Query
* Formdata/Multipart

For learning purposes, we will demonstrate the javadoc:value.Value[Value] features using query parameters, but keep in mind that these features apply to all parameter types.

===== Single value

Single values are retrieved via the `value()` or `[type]Value()` functions:

.Java
[source, java, role="primary"]
----
{
  get("/", ctx -> {
    String name = ctx.query("name").value();                          // <1>
    float score = ctx.query("score").floatValue();                    // <2>
    boolean enabled = ctx.query("enabled").booleanValue();            // <3>
    BigDecimal decimal = ctx.query("decimal").value(BigDecimal::new); // <4>
    // ...
  });
}
----

.Kotlin
[source, kotlin, role="secondary"]
----
{
  get("/") {
    val name = ctx.query("name").value()                     // <1>
    val score = ctx.query("score").floatValue()              // <2>
    val enabled = ctx.query("enabled").booleanValue()        // <3>
    val decimal = ctx.query("decimal").value(::BigDecimal)   // <4>
    // ...
  }
}
----

The `value()` family of methods **always** expects a value to exist. If the value is missing or cannot be converted to the requested type, a `400 Bad Request` response is generated. Therefore, single-value parameters are implicitly **required**:

<1> Access parameter `name` as `String`:
* `/?name=foo` => `foo`
* `/` => `Bad Request(400): Missing value: "name"`

<2> Access parameter `score` as `float`:
* `/?score=1` => `1.0`
* `/?score=string` => `Bad Request(400)` (Type mismatch)
* `/` => `Bad Request(400)`

<3> Access parameter `enabled` as `boolean`:
* `/?enabled=true` => `true`
* `/?enabled=string` => `Bad Request(400)`
* `/` => `Bad Request(400)`

<4> Access parameter `decimal` and convert it to a custom type (`BigDecimal`):
* `/?decimal=2.3` => `2.3`
* `/?decimal=string` => `Bad Request(400)`
* `/` => `Bad Request(400)`

===== Default and Optional value

You can handle optional parameters by providing a default value or requesting an `Optional` object:

.Java
[source, java,role="primary"]
----
{
  get("/search", ctx -> {
    String q1 = ctx.query("q").value("*:*");           // <1>
    Optional<String> q2 = ctx.query("q").toOptional(); // <2>
    return q1;
  });
}
----

.Kotlin
[source, kotlin,role="secondary"]
----
{
  get("/search") {
    val q1 = ctx.query("q").value("*:*")     // <1>
    val q2 = ctx.query("q").toOptional()     // <2>
    q1
  }
}
----

<1> Retrieve variable `q` as a `String` with a fallback default value of `*:*`.
* `/search?q=foo` => `foo`
* `/search` => `*:*`

<2> Retrieve variable `q` wrapped in an `Optional<String>`:
* `/search?q=foo` => `Optional[foo]`
* `/search` => `Optional.empty`

===== Multiple values

Multiple values for a single parameter key can be retrieved as Collections:

* javadoc:value.Value[toList]: Returns a `java.util.List`
* javadoc:value.Value[toSet]: Returns a `java.util.Set`

.Java
[source, java,role="primary"]
----
{
  get("/", ctx -> {
    List<String> q = ctx.query("q").toList();                           // <1>
    List<Integer> n = ctx.query("n").toList(Integer.class);             // <2>
    List<BigDecimal> d = ctx.query("d").toList(BigDecimal::new);        // <3>
    // ...
  });
}
----

.Kotlin
[source, kotlin,role="secondary"]
----
{
  get("/") {
    val q = ctx.query("q").toList()                     // <1>
    val n = ctx.query("n").toList(Integer::class.java)  // <2>
    val d = ctx.query("d").toList(::BigDecimal)         // <3>
    // ...
  }
}
----

<1> Parameter `q` as `List<String>`:
* `/` => `[]`
* `/?q=foo` => `[foo]`
* `/?q=foo&q=bar` => `[foo, bar]`

<2> Parameter `n` as `List<Integer>`:
* `/` => `[]`
* `/?n=1&n=2` => `[1, 2]`

<3> Parameter `d` mapped to `List<BigDecimal>`:
* `/` => `[]`
* `/?d=1.5&d=2.0` => `[1.5, 2.0]`

===== Structured data

The javadoc:value.Value[Value API] allows you to traverse and parse deeply nested structured data from a request.

Given the query: `/?user.name=root&user.pass=pass`

.Traversal
[source, java, role="primary"]
----
{
  get("/", ctx -> {
    Value user = ctx.query("user");                  // <1>
    String name  = user.get("name").value();         // <2>
    String pass  = user.get("pass").value();         // <3>
    String email = user.get("email").value("none");  // <4>
    // ...
  });
}
----

.Kotlin
[source, kotlin, role="secondary"]
----
{
  get("/") {
    val user = ctx.query("user")              // <1>
    val name  = user["name"].value()          // <2>
    val pass  = user["pass"].value()          // <3>
    val email = user["email"].value("none")   // <4>
    // ...
  }
}
----

<1> Retrieves the `user` node.
<2> Extracts `name` from the `user` node.
<3> Extracts `pass` from the `user` node.
<4> Safely extracts `email` with a fallback default.

The javadoc:value.Value[get, java.lang.String] method takes a `path` and returns a nested `Value` node, which may or may not exist.

====== Syntax

The structured data decoder supports both `dot` and `bracket` notation:

.Dot notation
----
?member.firstname=Pedro&member.lastname=Picapiedra
----

.Bracket object notation
----
?member[firstname]=Pedro&member[lastname]=Picapiedra
----

.Bracket array notation for tabular data
----
?members[0]firstname=Pedro&members[0]lastname=Picapiedra
----

====== POJO Binding

The data decoder can automatically reconstruct POJOs (Plain Old Java Objects) from:

* URL-encoded Queries
* `application/x-www-form-urlencoded` Form data
* `multipart/form-data` Forms

.Example Models
[source, java, role="primary"]
----
class Member {
  public final String firstname;
  public final String lastname;

  public Member(String firstname, String lastname) {
    this.firstname = firstname;
    this.lastname = lastname;
  }
}

class Group {
  public final String id;
  public final List<Member> members;

  public Group(String id, List<Member> members) {
    this.id = id;
    this.members = members;
  }
}
----

.Kotlin
[source, kotlin, role="secondary"]
----
class Member(val firstname: String, val lastname: String)

class Group(val id: String, val members: List<Member>)
----

**Binding a single Member:**
`/?firstname=Pedro&lastname=Picapiedra`

.Java
[source, java,role="primary"]
----
{
  get("/", ctx -> {
    Member member = ctx.query(Member.class);
    // ...
  });
}
----

.Kotlin
[source, kotlin,role="secondary"]
----
{
  get("/") {
    val member = ctx.query<Member>()
    // ...
  }
}
----

**Binding a nested Member from a root node:**
`/?member.firstname=Pedro&member.lastname=Picapiedra`

.Java
[source, java,role="primary"]
----
{
  get("/", ctx -> {
    Member member = ctx.query("member").to(Member.class);
    // ...
  });
}
----

.Kotlin
[source, kotlin,role="secondary"]
----
{
  get("/") {
    val member = ctx.query("member").to<Member>()
    // ...
  }
}
----

**Binding tabular/array data:**
`/?[0]firstname=Pedro&[0]lastname=Picapiedra&[1]firstname=Pablo&[1]lastname=Marmol`

.Java
[source, java,role="primary"]
----
{
  get("/", ctx -> {
    List<Member> members = ctx.query().toList(Member.class);
    // ...
  });
}
----

.Kotlin
[source, kotlin,role="secondary"]
----
{
  get("/") {
    val members = ctx.query<List<Member>>()
    // ...
  }
}
----

**Binding complex nested hierarchies:**
`/?id=flintstones&members[0]firstname=Pedro&members[0]lastname=Picapiedra`

.Java
[source, java,role="primary"]
----
{
  get("/", ctx -> {
    Group group = ctx.query(Group.class);
    // ...
  });
}
----

.Kotlin
[source, kotlin,role="secondary"]
----
{
  get("/") {
    val group = ctx.query<Group>()
    // ...
  }
}
----

**POJO Binding Rules:**
The target POJO must follow one of these rules:

* Have a zero-argument (default) constructor.
* Have exactly one constructor.
* Have multiple constructors, but only one is annotated with `@Inject`.

The decoder maps HTTP parameters in the following order:
1. Constructor arguments
2. Setter methods

If an HTTP parameter name is not a valid Java identifier (e.g., `first-name`), you must map it using the `@Named` annotation:

.Java
[source, java,role="primary"]
----
class Member {
  public final String firstname;
  public final String lastname;
  
  public Member(@Named("first-name") String firstname, @Named("last-name") String lastname) {
    this.firstname = firstname;
    this.lastname = lastname;
  }
}
----

.Kotlin
[source,kotlin,role="secondary"]
----
class Member(@Named("first-name") val firstname: String, @Named("last-name") val lastname: String)
----

===== Value Factory

The javadoc:value.ValueFactory[] allows you to register new type conversions or override existing ones globally.

.Java
[source,java,role="primary"]
----
{
    var valueFactory = getValueFactory();
    valueFactory.put(MyBean.class, new MyBeanConverter());
}

import io.jooby.value.ValueConverter;
  
class MyBeanConverter implements ValueConverter {
  @Override
  public Object convert(Type type, Value node, ConversionHint hint) {
    // Logic to convert the 'node' into MyBean.class
    return new MyBean();
  }
}
----

.Kotlin
[source,kotlin,role="secondary"]
----
{
    valueFactory.put(MyBean::class.java, MyBeanConverter())
}

import io.jooby.value.ValueConverter
  
class MyBeanConverter : ValueConverter {
  override fun convert(type: Type, node: Value, hint: ConversionHint): Any {
    // Logic to convert the 'node' into MyBean::class.java
    return MyBean()
  }
}
----
