blob: 086f0b392039e3a3be22fbc2b4cb6f5c313e05b3 [file] [log] [blame]
<!--
/***************************************************************************************************************************
* 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.
***************************************************************************************************************************/
-->
OpenAPI Parsers
<p>
The {@link oaj.oapi.OpenApiParser} class is used to convert HTTP parts back into POJOs.
</p>
<p>
The following is the previous example of a schema that defines the format of a pipe-delimited list of comma-delimited numbers (e.g. <js>"1,2,3|4,5,6|7,8,9"</js>):
</p>
<p class='bpcode w800'>
<jk>import static</jk> org.apache.juneau.httppart.HttpPartSchema.*;
HttpPartSchema schema = <jsm>create</jsm>()
.items(
<jsm>create</jsm>()
.collectionFormat(<js>"pipes"</js>)
.items(
<jsm>create</jsm>()
.collectionFormat(<js>"csv"</js>)
.type(<js>"integer"</js>)
.format(<js>"int64"</js>)
.minimum(<js>"0"</js>)
.maximum(<js>"100"</js>)
.minLength(1)
.maxLength=(10)
)
)
.build();
</p>
<p>
The following code shows how the schema above can be used to parse our input into a POJO:
</p>
<p class='bpcode w800'>
<jc>// Our input being parsed.</jc>
String input = <js>"1,2,3|4,5,6|7,8,9"</js>
<jc>// The parser to use.</jc>
HttpPartParser p = OpenApiParser.<jsf>DEFAULT</jsf>;
<jc>// Convert string to a POJO.</jc>
<jk>try</jk> {
Long[][] pojo = p.parse(schema, input, Long[][].<jk>class</jk>);
} <jk>catch</jk> (SchemaValidationException e) {
<jc>// Oops, one of the restrictions were not met.</jc>
}
</p>
<p>
As a general rule, any POJO convertible from the intermediate type for the <c>type/format</c> of the schema can
be parsed using the OpenAPI parser.
Here are the rules of POJO types allowed for various type/format combinations:
</p>
<table class='styled w800'>
<tr><th>Type</th><th>Format</th><th>Valid parameter types</th></tr>
<tr class='dark bb'>
<td rowspan='4'><c>string</c> or empty</td>
<td><c>byte<br>binary<br>binary-spaced</c></td>
<td>
<ul>
<li><c><jk>byte</jk>[]</c> (default)
<li>{@link java.io.InputStream} - Returns a {@link java.io.ByteArrayInputStream}.
<li>{@link java.io.Reader} - Returns a {@link java.io.InputStreamReader} wrapped around a {@link java.io.ByteArrayInputStream}.
<li>{@link java.lang.String} - Constructed using {@link java.lang.String#String(byte[])}.
<li>{@link java.lang.Object} - Returns the default <c><jk>byte</jk>[]</c>.
<li>Any POJO transformable from a <c><jk>byte</jk>[]</c> (via constructors or static create methods).
</ul>
</td>
</tr>
<tr class='dark bb'>
<td><c>date<br>date-time</c></td>
<td>
<ul>
<li>{@link java.util.Calendar} (default)
<li>{@link java.util.Date}
<li>{@link java.util.GregorianCalendar}
<li>{@link java.lang.String} - Converted using {@link java.util.Calendar#toString()}.
<li>{@link java.lang.Object} - Returns the default {@link java.util.Calendar}.
<li>Any POJO transformable from a {@link java.util.Calendar} (via constructors or static create methods).
</ul>
</td>
</tr>
<tr class='dark bb'>
<td><c>uon</c></td>
<td>
<ul>
<li>Any {@doc PojoCategories Parsable POJO} type.
</ul>
</td>
</tr>
<tr class='dark bb'>
<td>empty</td>
<td>
<ul>
<li>{@link java.lang.String} (default)
<li>{@link java.lang.Object} - Returns the default {@link java.lang.String}.
<li>Any POJO transformable from a {@link java.lang.String} (via constructors, static create methods, or swaps).
</ul>
</td>
</tr>
<tr class='light bb'>
<td rowspan='1'><c>boolean</c></td>
<td>empty</td>
<td>
<ul>
<li>{@link java.lang.Boolean} (default)
<li><jk>boolean</jk>
<li>{@link java.lang.String}
<li>{@link java.lang.Object} - Returns the default {@link java.lang.Boolean}.
<li>Any POJO transformable from a {@link java.lang.Boolean} (via constructors, static create methods, or swaps).
</ul>
</td>
</tr>
<tr class='dark bb'>
<td rowspan='2'><c>integer</c></td>
<td><c>int32</c></td>
<td>
<ul>
<li>{@link java.lang.Integer} (default)
<li>Any subclass of {@link java.lang.Number}
<li>Any primitive number: (e.g <jk>int</jk>, <jk>float</jk>...)
<li>{@link java.lang.String}
<li>{@link java.lang.Object} - Returns the default {@link java.lang.Integer}.
<li>Any POJO transformable from an {@link java.lang.Integer} (via constructors, static create methods, or swaps).
</ul>
</td>
</tr>
<tr class='dark bb'>
<td><c>int64</c></td>
<td>
<ul>
<li>{@link java.lang.Long} (default)
<li>Any subclass of {@link java.lang.Number}
<li>Any primitive number: (e.g <jk>int</jk>, <jk>float</jk>...)
<li>{@link java.lang.String}
<li>{@link java.lang.Object} - Returns the default {@link java.lang.Long}.
<li>Any POJO transformable from an {@link java.lang.Long} (via constructors, static create methods, or swaps).
</ul>
</td>
</tr>
<tr class='light bb'>
<td rowspan='2'><c>number</c></td>
<td><c>float</c></td>
<td>
<ul>
<li>{@link java.lang.Float} (default)
<li>Any subclass of {@link java.lang.Number}
<li>Any primitive number: (e.g <jk>int</jk>, <jk>float</jk>...)
<li>{@link java.lang.String}
<li>{@link java.lang.Object} - Returns the default {@link java.lang.Float}.
<li>Any POJO transformable from an {@link java.lang.Float} (via constructors, static create methods, or swaps).
</ul>
</td>
</tr>
<tr class='light bb'>
<td><c>double</c></td>
<td>
<ul>
<li>{@link java.lang.Double} (default)
<li>Any subclass of {@link java.lang.Number}
<li>Any primitive number: (e.g <jk>int</jk>, <jk>float</jk>...)
<li>{@link java.lang.String}
<li>{@link java.lang.Object} - Returns the default {@link java.lang.Double}.
<li>Any POJO transformable from an {@link java.lang.Double} (via constructors, static create methods, or swaps).
</ul>
</td>
</tr>
<tr class='dark bb'>
<td rowspan='2'><c>array</c></td>
<td>empty</td>
<td>
<ul>
<li>Arrays or Collections of anything on this list.
<li>Any POJO transformable from arrays of the default types (e.g. <c>Integer[]</c>, <c>Boolean[][]</c>, etc...).
</ul>
</td>
</tr>
<tr class='dark bb'>
<td><c>uon</c></td>
<td>
<ul>
<li>Any {@doc PojoCategories Parsable POJO} type.
</ul>
</td>
</tr>
<tr class='light bb'>
<td rowspan='2'><c>object</c></td>
<td>empty</td>
<td>
<ul>
<li><c>Map&lt;String,Object&gt;</c> (default)
<li>Beans with properties of anything on this list.
<li>Maps with string keys.
</ul>
</td>
</tr>
<tr class='light bb'>
<td><c>uon</c></td>
<td>
<ul>
<li>Any {@doc PojoCategories Parsable POJO} type.
</ul>
</td>
</tr>
</table>
<p>
Additionally, any of the type above can also be wrapped as {@link java.util.Optional Optionals}.
</p>
<p>
For arrays, an example of "Any POJO transformable from arrays of the default types" is:
</p>
<p class='bpcode w800'>
<jc>// Sample POJO class convertable from a Long[][].</jc>
<jk>public class</jk> MyPojo {
<jc>// Constructor used by parser.</jc>
<jk>public</jk> MyPojo(Long[][] from2dLongs) {...}
}
</p>
<p>
In the example above, our POJO class can be constructed from our pipe-delimited list of comma-delimited numbers:
</p>
<p class='bpcode w800'>
<jc>// Our input being parsed.</jc>
String input = <js>"1,2,3|4,5,6|7,8,9"</js>
<jc>// The parser to use.</jc>
HttpPartParser p = OpenApiParser.<jsf>DEFAULT</jsf>;
<jc>// Convert string to a POJO.</jc>
<jk>try</jk> {
MyPojo pojo = p.parse(schema, input, MyPojo.<jk>class</jk>);
} <jk>catch</jk> (SchemaValidationException e) {
<jc>// Oops, one of the restrictions were not met.</jc>
}
</p>
<p>
Just like serialization, the <c>object</c> type is not officially part of the OpenAPI standard, but
Juneau supports parsing HTTP parts in UON notation to Maps and beans.
</p>
<p>
The following shows an example of a bean with several properties of various types.
</p>
<p class='bpcode w800'>
<jk>public class</jk> MyBean {
<jk>public</jk> String <jf>f1</jf>;
<jk>public byte</jk>[] <jf>f2</jf>;
<jk>public byte</jk>[] <jf>f3</jf>;
<jk>public byte</jk>[] <jf>f4</jf>;
<jk>public</jk> Calendar <jf>f5</jf>;
<jk>public</jk> String <jf>f6</jf>;
<jk>public int</jk> <jf>f7</jf>;
<jk>public</jk> Long <jf>f8</jf>;
<jk>public float</jk> <jf>f9</jf>;
<jk>public</jk> Double <jf>f10</jf>;
<jk>public</jk> Boolean <jf>f11</jf>;
<jk>public</jk> Object <jf>fExtra</jf>;
}
</p>
<p>
We define the following schema again:
</p>
<p class='bpcode w800'>
<jk>import static</jk> org.apache.juneau.httppart.HttpPartSchema.*;
HttpPartSchema ps = <jsm>schema</jsm>(<js>"object"</js>)
.property(<js>"f1"</js>, <jsm>schema</jsm>(<js>"string"</js>))
.property(<js>"f2"</js>, <jsm>schema</jsm>(<js>"string"</js>, <js>"byte"</js>))
.property(<js>"f3"</js>, <jsm>schema</jsm>(<js>"string"</js>, <js>"binary"</js>))
.property(<js>"f4"</js>, <jsm>schema</jsm>(<js>"string"</js>, <js>"binary-spaced"</js>))
.property(<js>"f5"</js>, <jsm>schema</jsm>(<js>"string"</js>, <js>"date-time"</js>))
.property(<js>"f6"</js>, <jsm>schema</jsm>(<js>"string"</js>, "<js>uon"</js>))
.property(<js>"f7"</js>, <jsm>schema</jsm>(<js>"integer"</js>))
.property(<js>"f8"</js>, <jsm>schema</jsm>(<js>"integer"</js>, <js>"int64"</js>))
.property(<js>"f9"</js>, <jsm>schema</jsm>(<js>"number"</js>))
.property(<js>"f10"</js>, <jsm>schema</jsm>(<js>"number"</js>, <js>"double"</js>))
.property(<js>"f11"</js>, <jsm>schema</jsm>(<js>"boolean"</js>))
.additionalProperties(<jsm>schema</jsm>(<js>"integer"</js>))
.build();
</p>
<p>
Then we parse our input into our POJO:
</p>
<p class='bpcode w800'>
String input =
<js>"(f1=foo,f2=Zm9v,f3=666F6F,f4='66 6F 6F',f5=2012-12-21T12:34:56Z,f6=foo,"</js>
+ <js>"f7=1,f8=2,f9=1.0,f10=1.0,f11=true,fExtra=1)"</js>;
HttpPartParser p = OpenApiParser.<jsf>DEFAULT</jsf>;
MyBean b = p.parse(schema, input, MyBean.<jk>class</jk>);
</p>
<p>
Note that serializing into generic <c>Object</c> properties would have produced similar results:
</p>
<p class='bpcode w800'>
<jk>public class</jk> MyBean {
<jk>public</jk> Object <jf>f1</jf>;
<jk>public</jk> Object <jf>f2</jf>;
<jk>public</jk> Object <jf>f3</jf>;
<jk>public</jk> Object <jf>f4</jf>;
<jk>public</jk> Object <jf>f5</jf>;
<jk>public</jk> Object <jf>f6</jf>;
<jk>public</jk> Object <jf>f7</jf>;
<jk>public</jk> Object <jf>f8</jf>;
<jk>public</jk> Object <jf>f9</jf>;
<jk>public</jk> Object <jf>f10</jf>;
<jk>public</jk> Object <jf>f11</jf>;
<jk>public</jk> Object <jf>fExtra</jf>;
}
</p>
<p>
We can also parse into Maps as well:
</p>
<p class='bpcode w800'>
String input =
<js>"(f1=foo,f2=Zm9v,f3=666F6F,f4='66 6F 6F',f5=2012-12-21T12:34:56Z,f6=foo,"</js>
+ <js>"f7=1,f8=2,f9=1.0,f10=1.0,f11=true,fExtra=1)"</js>;
HttpPartParser p = OpenApiParser.<jsf>DEFAULT</jsf>;
ObjectMap m = p.parse(schema, input, ObjectMap.<jk>class</jk>);
</p>
<ul class='notes'>
<li>
Array properties can also use CSV/SSV/PIPES for array notation.
<br>Various notations can be mixed throughout.
<li>
Schemas and POJOs can be defined arbitrarily deep.
<li>
Schemas are optional.
They can be skipped or partially defined.
<li>
We make our best attempt to convert the output to the matching type.
However, you will get <c>ParseExceptions</c> if you attempt an impossible conversion.
(e.g. trying to parse the string "foo" into a boolean).
</ul>