Skip to content

Commit f87b314

Browse files
authored
Merge branch 'master' into fix/ref-validator
2 parents dc9137f + 278533e commit f87b314

23 files changed

+1236
-359
lines changed

CHANGELOG.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,57 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
99

1010
### Changed
1111

12+
## 1.0.13 - 2019-06-05
13+
14+
### Added
15+
16+
### Changed
17+
18+
- fixes #158 date-time format should consider colon in timezone optional. Thanks @chuwy
19+
20+
## 1.0.12 - 2019-05-30
21+
22+
### Added
23+
24+
### Changed
25+
26+
- fixes #155 Fix date-time validation. Thanks @jiachen1120
27+
28+
## 1.0.11 - 2019-05-28
29+
30+
### Added
31+
32+
### Changed
33+
34+
- fixes #151 add validation for string type uuid. Thanks @chenyan71
35+
36+
## 1.0.10 - 2019-05-22
37+
38+
### Added
39+
40+
### Changed
41+
42+
- fixes #138 validation of date fields. Thanks @jiachen1120
43+
44+
## 1.0.9 - 2019-05-21
45+
46+
### Added
47+
48+
### Changed
49+
50+
- fixes #147 Fails to validate MIN and MAX when number type is converted to BigInteger. Thanks @jiachen1120
51+
52+
## 1.0.8 - 2019-05-17
53+
54+
### Added
55+
56+
### Changed
57+
58+
- fixes #145 Fix bug parsing array query params when only one item present. Thanks @jiachen1120
59+
- fixes #142 validation for enum object type. Thanks @jiachen1120
60+
- fixes #136 Maps of URLs can have performance impacts. Thanks @rhwood
61+
- fixes #134 $ref external schema references do not use URL mappings. Thanks @rhwood
62+
1263
## 1.0.7 - 2019-04-29
1364

1465
### Added

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ Add the following to your `pom.xml`:
5151
<dependency>
5252
<groupId>com.networknt</groupId>
5353
<artifactId>json-schema-validator</artifactId>
54-
<version>1.0.7</version>
54+
<version>1.0.13</version>
5555
</dependency>
5656
```
5757

pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
<modelVersion>4.0.0</modelVersion>
2020
<groupId>com.networknt</groupId>
2121
<artifactId>json-schema-validator</artifactId>
22-
<version>1.0.7</version>
22+
<version>1.0.13</version>
2323
<packaging>bundle</packaging>
2424
<description>A json schema validator that supports draft v4</description>
2525
<url>https://github.com/networknt/json-schema-validator</url>
@@ -61,7 +61,7 @@
6161
<java.version>1.6</java.version>
6262
<java.testversion>1.7</java.testversion>
6363
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
64-
<version.jackson>2.9.8</version.jackson>
64+
<version.jackson>2.9.9</version.jackson>
6565
<version.slf4j>1.7.25</version.slf4j>
6666
<version.common-lang3>3.5</version.common-lang3>
6767
<version.logback>1.2.3</version.logback>
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
/*
2+
* Copyright (c) 2016 Network New Technologies Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.networknt.schema;
18+
19+
import com.fasterxml.jackson.databind.JsonNode;
20+
import org.slf4j.Logger;
21+
import org.slf4j.LoggerFactory;
22+
23+
import java.text.ParsePosition;
24+
import java.text.SimpleDateFormat;
25+
import java.util.Collections;
26+
import java.util.LinkedHashSet;
27+
import java.util.Set;
28+
import java.util.regex.Matcher;
29+
import java.util.regex.Pattern;
30+
31+
public class DateTimeValidator extends BaseJsonValidator implements JsonValidator {
32+
private static final Logger logger = LoggerFactory.getLogger(DateTimeValidator.class);
33+
34+
private final String formatName;
35+
private final String DATE = "date";
36+
private final String DATETIME = "date-time";
37+
38+
private static final Pattern RFC3339_PATTERN = Pattern.compile(
39+
"^(\\d{4})-(\\d{2})-(\\d{2})" // yyyy-MM-dd
40+
+ "([Tt](\\d{2}):(\\d{2}):(\\d{2})(\\.\\d+)?)?" // 'T'HH:mm:ss.milliseconds
41+
+ "([Zz]|([+-])(\\d{2}):?(\\d{2}))?");
42+
43+
public DateTimeValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext, String formatName) {
44+
super(schemaPath, schemaNode, parentSchema, ValidatorTypeCode.DATETIME, validationContext);
45+
this.formatName = formatName;
46+
parseErrorCode(getValidatorType().getErrorCodeKey());
47+
}
48+
49+
public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String at) {
50+
debug(logger, node, rootNode, at);
51+
52+
Set<ValidationMessage> errors = new LinkedHashSet<ValidationMessage>();
53+
54+
JsonType nodeType = TypeFactory.getValueNodeType(node);
55+
if (nodeType != JsonType.STRING) {
56+
return errors;
57+
}
58+
if (!isLegalDateTime(node.textValue())) {
59+
errors.add(buildValidationMessage(at, node.textValue(), formatName));
60+
}
61+
return Collections.unmodifiableSet(errors);
62+
}
63+
64+
private boolean isLegalDateTime(String string) {
65+
Matcher matcher = RFC3339_PATTERN.matcher(string);
66+
StringBuilder pattern = new StringBuilder();
67+
StringBuilder dateTime = new StringBuilder();
68+
// Validate the format
69+
if (!matcher.matches()) {
70+
logger.error("Failed to apply RFC3339 pattern on " + string);
71+
return false;
72+
}
73+
// Validate the date/time content
74+
String year = matcher.group(1);
75+
String month = matcher.group(2);
76+
String day = matcher.group(3);
77+
dateTime.append(year).append('-').append(month).append('-').append(day);
78+
pattern.append("yyyy-MM-dd");
79+
80+
boolean isTimeGiven = matcher.group(4) != null;
81+
String timeZoneShiftRegexGroup = matcher.group(9);
82+
boolean isTimeZoneShiftGiven = timeZoneShiftRegexGroup != null;
83+
String hour = null;
84+
String minute = null;
85+
String second = null;
86+
String milliseconds = null;
87+
String timeShiftHour = null;
88+
String timeShiftMinute = null;
89+
90+
if (!isTimeGiven && DATETIME.equals(formatName) || (isTimeGiven && DATE.equals(formatName))) {
91+
logger.error("The supplied date/time format type does not match the specification, expected: " + formatName);
92+
return false;
93+
}
94+
95+
if (!isTimeGiven && isTimeZoneShiftGiven) {
96+
logger.error("Invalid date/time format, cannot specify time zone shift" +
97+
" without specifying time: " + string);
98+
return false;
99+
}
100+
101+
if (isTimeGiven) {
102+
hour = matcher.group(5);
103+
minute = matcher.group(6);
104+
second = matcher.group(7);
105+
dateTime.append('T').append(hour).append(':').append(minute).append(':').append(second);
106+
pattern.append("'T'HH:mm:ss");
107+
if (matcher.group(8) != null) {
108+
// Normalize milliseconds to 3-length digit
109+
milliseconds = matcher.group(8);
110+
if (milliseconds.length() > 4) {
111+
milliseconds = milliseconds.substring(0, 4);
112+
} else {
113+
while (milliseconds.length() < 4) {
114+
milliseconds += "0";
115+
}
116+
}
117+
dateTime.append(milliseconds);
118+
pattern.append(".SSS");
119+
}
120+
}
121+
122+
if (isTimeGiven && isTimeZoneShiftGiven) {
123+
if (Character.toUpperCase(timeZoneShiftRegexGroup.charAt(0)) == 'Z') {
124+
dateTime.append('Z');
125+
pattern.append("'Z'");
126+
} else {
127+
timeShiftHour = matcher.group(11);
128+
timeShiftMinute = matcher.group(12);
129+
dateTime.append(matcher.group(10).charAt(0)).append(timeShiftHour).append(':').append(timeShiftMinute);
130+
pattern.append("XXX");
131+
}
132+
}
133+
return validateDateTime(dateTime.toString(), pattern.toString());
134+
}
135+
136+
private boolean validateDateTime(String dateTime, String pattern) {
137+
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
138+
sdf.setLenient(false);
139+
return sdf.parse(dateTime, new ParsePosition(0)) != null;
140+
}
141+
}

src/main/java/com/networknt/schema/FormatKeyword.java

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ public class FormatKeyword implements Keyword {
2626
private final ValidatorTypeCode type;
2727
private final Map<String, Format> formats;
2828

29+
private final String DATE = "date";
30+
private final String DATE_TIME = "date-time";
31+
private final String UUID = "uuid";
32+
2933
public FormatKeyword(ValidatorTypeCode type, Map<String, Format> formats) {
3034
this.type = type;
3135
this.formats = formats;
@@ -34,23 +38,26 @@ public FormatKeyword(ValidatorTypeCode type, Map<String, Format> formats) {
3438
Collection<Format> getFormats() {
3539
return Collections.unmodifiableCollection(formats.values());
3640
}
37-
41+
3842
@Override
3943
public JsonValidator newValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext)
4044
throws Exception {
4145
Format format = null;
4246
if (schemaNode != null && schemaNode.isTextual()) {
4347
String formatName = schemaNode.textValue();
4448
format = formats.get(formatName);
49+
// Validate date and time separately
50+
if (formatName.equals(DATE) || formatName.equals(DATE_TIME)) {
51+
return new DateTimeValidator(schemaPath, schemaNode, parentSchema, validationContext, formatName);
52+
} else if (formatName.equals(UUID)) {
53+
return new UUIDValidator(schemaPath, schemaNode, parentSchema, validationContext, formatName);
54+
}
4555
}
46-
4756
return new FormatValidator(schemaPath, schemaNode, parentSchema, validationContext, format);
4857
}
49-
58+
5059
@Override
5160
public String getValue() {
5261
return type.getValue();
5362
}
54-
55-
5663
}

0 commit comments

Comments
 (0)