Skip to content
This repository was archived by the owner on Nov 24, 2022. It is now read-only.

Commit 7160ffb

Browse files
authored
Merge pull request swagger-api#1062 from kerrykimbrough/issue-1049
(issue swagger-api#1049) OpenAPIDeserializer: Rework handling of formatted string values using getDecodedObject()
2 parents a41877b + 49fc009 commit 7160ffb

2 files changed

Lines changed: 360 additions & 3 deletions

File tree

modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/util/OpenAPIDeserializer.java

Lines changed: 137 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,11 @@
1717
import io.swagger.v3.oas.models.info.Contact;
1818
import io.swagger.v3.oas.models.info.Info;
1919
import io.swagger.v3.oas.models.media.ArraySchema;
20+
import io.swagger.v3.oas.models.media.ByteArraySchema;
2021
import io.swagger.v3.oas.models.media.ComposedSchema;
2122
import io.swagger.v3.oas.models.media.Content;
23+
import io.swagger.v3.oas.models.media.DateSchema;
24+
import io.swagger.v3.oas.models.media.DateTimeSchema;
2225
import io.swagger.v3.oas.models.media.Discriminator;
2326
import io.swagger.v3.oas.models.media.Encoding;
2427
import io.swagger.v3.oas.models.media.MediaType;
@@ -56,10 +59,13 @@
5659
import java.net.URI;
5760
import java.net.URISyntaxException;
5861
import java.net.URL;
62+
import java.text.ParseException;
5963
import java.util.*;
64+
import java.util.regex.Matcher;
6065
import java.util.regex.Pattern;
6166
import java.util.stream.Collectors;
6267
import java.util.stream.Stream;
68+
import static java.util.Calendar.*;
6369

6470

6571
public class OpenAPIDeserializer {
@@ -94,6 +100,9 @@ public class OpenAPIDeserializer {
94100
private static final String COOKIE_PARAMETER = "cookie";
95101
private static final String PATH_PARAMETER = "path";
96102
private static final String HEADER_PARAMETER = "header";
103+
private static final Pattern RFC3339_DATE_TIME_PATTERN = Pattern.compile( "^(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2}):(\\d{2})(\\.\\d+)?((Z)|([+-]\\d{2}:\\d{2}))$");
104+
private static final Pattern RFC3339_DATE_PATTERN = Pattern.compile( "^(\\d{4})-(\\d{2})-(\\d{2})$");
105+
97106
private Components components;
98107
private final Set<String> operationIDs = new HashSet<>();
99108

@@ -2201,8 +2210,13 @@ public Schema getSchema(ObjectNode node, String location, ParseResult result){
22012210
for (JsonNode n : enumArray) {
22022211
if (n.isNumber()) {
22032212
schema.addEnumItemObject(n.numberValue());
2204-
}else if (n.isValueNode()) {
2205-
schema.addEnumItemObject(n.asText());
2213+
} else if (n.isValueNode()) {
2214+
try {
2215+
schema.addEnumItemObject( getDecodedObject( schema, n.asText(null)));
2216+
}
2217+
catch( ParseException e) {
2218+
result.invalidType( location, String.format( "enum=`%s`", e.getMessage()), schema.getFormat(), n);
2219+
}
22062220
} else {
22072221
result.invalidType(location, "enum", "value", n);
22082222
}
@@ -2291,7 +2305,12 @@ public Schema getSchema(ObjectNode node, String location, ParseResult result){
22912305
}else if(schema.getType().equals("string")) {
22922306
value = getString("default", node, false, location, result);
22932307
if (value != null) {
2294-
schema.setDefault(value);
2308+
try {
2309+
schema.setDefault( getDecodedObject( schema, value));
2310+
}
2311+
catch( ParseException e) {
2312+
result.invalidType( location, String.format( "default=`%s`", e.getMessage()), schema.getFormat(), node);
2313+
}
22952314
}
22962315
}else if(schema.getType().equals("boolean")) {
22972316
bool = getBoolean("default", node, false, location, result);
@@ -2375,6 +2394,121 @@ public Schema getSchema(ObjectNode node, String location, ParseResult result){
23752394
}
23762395

23772396

2397+
/**
2398+
* Decodes the given string and returns an object applicable to the given schema.
2399+
* Throws a ParseException if no applicable object can be recognized.
2400+
*/
2401+
private Object getDecodedObject( Schema schema, String objectString) throws ParseException {
2402+
Object object =
2403+
objectString == null?
2404+
null :
2405+
2406+
schema.getClass().equals( DateSchema.class)?
2407+
toDate( objectString) :
2408+
2409+
schema.getClass().equals( DateTimeSchema.class)?
2410+
toDateTime( objectString) :
2411+
2412+
schema.getClass().equals( ByteArraySchema.class)?
2413+
toBytes( objectString) :
2414+
2415+
objectString;
2416+
2417+
if( object == null && objectString != null) {
2418+
throw new ParseException( objectString, 0);
2419+
}
2420+
2421+
return object;
2422+
}
2423+
2424+
2425+
/**
2426+
* Returns the Date represented by the given RFC3339 date-time string.
2427+
* Returns null if this string can't be parsed as Date.
2428+
*/
2429+
private Date toDateTime( String dateString) {
2430+
// Note: For this conversion, regex matching is better than SimpleDateFormat, etc.
2431+
// Optional elements (e.g. milliseconds) are not directly handled by SimpleDateFormat.
2432+
// Also, SimpleDateFormat is not thread-safe.
2433+
Matcher matcher = RFC3339_DATE_TIME_PATTERN.matcher( dateString);
2434+
2435+
Date dateTime = null;
2436+
if( matcher.matches()) {
2437+
try {
2438+
String year = matcher.group(1);
2439+
String month = matcher.group(2);
2440+
String day = matcher.group(3);
2441+
String hour = matcher.group(4);
2442+
String min = matcher.group(5);
2443+
String sec = matcher.group(6);
2444+
String ms = matcher.group(7);
2445+
String zone = matcher.group(10);
2446+
2447+
Calendar calendar = Calendar.getInstance( TimeZone.getTimeZone( zone == null? "GMT" : "GMT" + zone));
2448+
calendar.set( YEAR, Integer.parseInt( year));
2449+
calendar.set( MONTH, Integer.parseInt( month) - 1);
2450+
calendar.set( DAY_OF_MONTH, Integer.parseInt( day));
2451+
calendar.set( HOUR_OF_DAY, Integer.parseInt( hour));
2452+
calendar.set( MINUTE, Integer.parseInt( min));
2453+
calendar.set( SECOND, Integer.parseInt( sec));
2454+
calendar.set( MILLISECOND, ms == null? 0 : (int) (Double.parseDouble( ms) * 1000));
2455+
2456+
dateTime = calendar.getTime();
2457+
}
2458+
catch( Exception ignore) {
2459+
}
2460+
}
2461+
2462+
return dateTime;
2463+
}
2464+
2465+
2466+
/**
2467+
* Returns the Date represented by the given RFC3339 full-date string.
2468+
* Returns null if this string can't be parsed as Date.
2469+
*/
2470+
private Date toDate( String dateString) {
2471+
Matcher matcher = RFC3339_DATE_PATTERN.matcher( dateString);
2472+
2473+
Date date = null;
2474+
if( matcher.matches()) {
2475+
String year = matcher.group(1);
2476+
String month = matcher.group(2);
2477+
String day = matcher.group(3);
2478+
2479+
try {
2480+
date=
2481+
new Calendar.Builder()
2482+
.setDate( Integer.parseInt( year), Integer.parseInt( month) - 1, Integer.parseInt( day))
2483+
.build()
2484+
.getTime();
2485+
}
2486+
catch( Exception ignore) {
2487+
}
2488+
}
2489+
2490+
return date;
2491+
}
2492+
2493+
2494+
/**
2495+
* Returns the byte array represented by the given base64-encoded string.
2496+
* Returns null if this string is not a valid base64 encoding.
2497+
*/
2498+
private byte[] toBytes( String byteString) {
2499+
byte[] bytes;
2500+
2501+
try {
2502+
bytes = Base64.getDecoder().decode( byteString);
2503+
}
2504+
catch( Exception e) {
2505+
bytes = null;
2506+
}
2507+
2508+
return bytes;
2509+
}
2510+
2511+
23782512

23792513

23802514
public Map<String, Example> getExamples(ObjectNode obj, String location, ParseResult result) {

0 commit comments

Comments
 (0)