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

Commit fdad4e4

Browse files
(issue swagger-api#1049) OpenAPIDeserializer: Rework handling of formatted string values using getDecodedObject()
1 parent aeb9def commit fdad4e4

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;
@@ -52,10 +55,13 @@
5255
import java.net.URI;
5356
import java.net.URISyntaxException;
5457
import java.net.URL;
58+
import java.text.ParseException;
5559
import java.util.*;
60+
import java.util.regex.Matcher;
5661
import java.util.regex.Pattern;
5762
import java.util.stream.Collectors;
5863
import java.util.stream.Stream;
64+
import static java.util.Calendar.*;
5965

6066

6167
public class OpenAPIDeserializer {
@@ -90,6 +96,9 @@ public class OpenAPIDeserializer {
9096
private static final String COOKIE_PARAMETER = "cookie";
9197
private static final String PATH_PARAMETER = "path";
9298
private static final String HEADER_PARAMETER = "header";
99+
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}))$");
100+
private static final Pattern RFC3339_DATE_PATTERN = Pattern.compile( "^(\\d{4})-(\\d{2})-(\\d{2})$");
101+
93102
private Components components;
94103
private final Set<String> operationIDs = new HashSet<>();
95104

@@ -2185,8 +2194,13 @@ public Schema getSchema(ObjectNode node, String location, ParseResult result){
21852194
for (JsonNode n : enumArray) {
21862195
if (n.isNumber()) {
21872196
schema.addEnumItemObject(n.numberValue());
2188-
}else if (n.isValueNode()) {
2189-
schema.addEnumItemObject(n.asText());
2197+
} else if (n.isValueNode()) {
2198+
try {
2199+
schema.addEnumItemObject( getDecodedObject( schema, n.asText(null)));
2200+
}
2201+
catch( ParseException e) {
2202+
result.invalidType( location, String.format( "enum=`%s`", e.getMessage()), schema.getFormat(), n);
2203+
}
21902204
} else {
21912205
result.invalidType(location, "enum", "value", n);
21922206
}
@@ -2275,7 +2289,12 @@ public Schema getSchema(ObjectNode node, String location, ParseResult result){
22752289
}else if(schema.getType().equals("string")) {
22762290
value = getString("default", node, false, location, result);
22772291
if (value != null) {
2278-
schema.setDefault(value);
2292+
try {
2293+
schema.setDefault( getDecodedObject( schema, value));
2294+
}
2295+
catch( ParseException e) {
2296+
result.invalidType( location, String.format( "default=`%s`", e.getMessage()), schema.getFormat(), node);
2297+
}
22792298
}
22802299
}else if(schema.getType().equals("boolean")) {
22812300
bool = getBoolean("default", node, false, location, result);
@@ -2359,6 +2378,121 @@ public Schema getSchema(ObjectNode node, String location, ParseResult result){
23592378
}
23602379

23612380

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

23632497

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

0 commit comments

Comments
 (0)