| <!-- |
| /*************************************************************************************************************************** |
| * 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<String,Object></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> |