Class Parsers
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic classProvides helpers to left-factor common prefixes followed by one or multiple optional suffixes. -
Field Summary
FieldsModifier and TypeFieldDescriptionParses a 4-digit hex BMP code unit.Parses an 8-digit hex Unicode code point (such as those following\Uin string escapes).Parses duration in the shorthand format of1.5h,30d,10m30setc.Parses double-precision numbers that support scientific notation, conforming to RFC 8259 (JSON spec).Parses unsigned decimal point numbers, e.g.,1.23,0.0,15,0.Parses unsigned decimal integer numbers, e.g.,15,0. -
Method Summary
Modifier and TypeMethodDescriptionReturns a leaf-level parser that matches an atomic regular expressionpattern.static <T> Parser<T> Returns a leaf-level parser that matches the givenpatternand transforms the 3 captured group values usingmapper.static <T> Parser<T> Returns a leaf-level parser that matches the givenpatternand transforms the 4 captured group values usingmapper.static <T> Parser<T> Returns a leaf-level parser that matches the givenpatternand transforms the 5 captured group values usingmapper.static <T> Parser<T> Returns a leaf-level parser that matches the givenpatternand transforms the 6 captured group values usingmapper.static <T> Parser<T> Returns a leaf-level parser that matches the givenpatternand transforms the 7 captured group values usingmapper.static <T> Parser<T> Returns a leaf-level parser that matches the givenpatternand transforms the 8 captured group values usingmapper.static <T> Parser<T> regex(String pattern, BiFunction<? super String, ? super String, ? extends T> mapper) Returns a leaf-level parser that matches the givenpatternand transforms the 2 captured group values usingmapper.static <T> Parser<T> Returns a leaf-level parser that matches the givenpatternand transforms the captured group value usingmapper.
-
Field Details
-
UNSIGNED_INTEGER
-
UNSIGNED_DECIMAL
Parses unsigned decimal point numbers, e.g.,1.23,0.0,15,0.To support signs, you can compose it like:
Parser<Double> signedDecimal = sequence( one('-').thenReturn(-1).orElse(1), UNSIGNED_DECIMAL.map(Double::parseDouble), (sign, num) -> sign * num); -
SIGNED_DOUBLE
Parses double-precision numbers that support scientific notation, conforming to RFC 8259 (JSON spec).E.g.,
123,-0.5,1e10,-1.23e+4,0.0e-5.The input string is parsed into a
Double. You can also call.source()if you prefer to obtain the raw matched string or parse into a different type such asBigDecimal:Parser<BigDecimal> bigDecimal = Parsers.SIGNED_DOUBLE.source().map(BigDecimal::new);Note that leading plus signs (e.g.,
+1), leading zeros on integers (e.g.,05), and missing integer or fractional parts (e.g.,.5or5.) are not allowed, as per the JSON standard. -
DURATION
Parses duration in the shorthand format of1.5h,30d,10m30setc.Matches one or more unit specs consisting of a positive decimal number followed by a unit suffix. For example:
"30s"->30 seconds"2h30m"->2 hours and 30 minutes"1w2d"->9 days (1 week + 2 days)"1.5h"->1 hour and 30 minutes
Supported units:
w(weeks) - treated as exactly 7 daysd(days) - treated as exactly 24 hoursh(hours)m(minutes)s(seconds)ms(milliseconds)us(microseconds)ns(nanoseconds)
Note:
- The duration components must be specified in strictly descending order of unit size
(e.g.,
"1d2h"is allowed, but"2h1d"or"1d1d"are not). - Only the last component can contain a decimal point (e.g.,
"1.5h"or"1h2.5m"are allowed, but"1.5h2m"is not). - Negative values (e.g.,
"-2s") are not supported.
-
BMP_CODE_UNIT
Parses a 4-digit hex BMP code unit.You can use it together with
Parser.quotedByWithEscapes()to parse unicode escapes like:Parser.quotedByWithEscapes('"', '"', Parser.one('u').then(BMP_CODE_UNIT).map(String::valueOf)); -
CODE_POINT
Parses an 8-digit hex Unicode code point (such as those following\Uin string escapes).The parsed integer is guaranteed to be a valid Unicode code point (between
0and0x10FFFF).You can use it together with
Parser.quotedByWithEscapes()to parse unicode escapes like:Parser<String> quotedStringWithUnicodeEscape = Parser.quotedByWithEscapes( '"', '"', Parser.one('U').then(CODE_POINT).map(Character::toString)); quotedStringWithUnicodeEscape.parse("\\U0001F600"); // returns "😀"
-
-
Method Details
-
regex
Returns a leaf-level parser that matches an atomic regular expressionpattern. For example, you could define a parser for US phone numbers using:Parser<String> usPhoneNumber = Parsers.regex("\\(\\d{3}\\)\\d{3}-\\d{4}"); usPhoneNumber.matches("(123)456-7890"); // => trueUseful when defining a compact, yet composite regex pattern that may otherwise require verbose boilerplate of
sequence(),anyOf()calls composed together. That said, refrain from creating complex regex patterns, and prefer using the declarativeParserAPI unless it's too verbose.WARNING: ReDoS Vulnerabilities & Disastrous Backtracking
Using regular expressions exposes the application to Regular Expression Denial of Service (ReDoS) attacks. In many regex engines (including
Pattern), even "simple" patterns can cause exponential backtracking if matched against malicious inputs designed to trigger worst-case paths.For example, the pattern
(a+)+or(a|ab)+can easily freeze a thread or crash a service with CPU exhaustion when attempting to match a string like"aaaaaaaaaaaaaaaaaaaaaaaaaaaa!". See the OWASP ReDoS Attack Reference for a detailed analysis of this issue.To avoid ReDoS and keep parsing execution linear and safe, prefer the declarative, backtracking-free
Parsercombinator API (using methods likefollowedBy(),sequence(),anyOf(), etc.) and only useregexon trusted input (such as a config file, command line tool etc).The pattern must be a compile-time constant, must not match the empty string, and must not contain anchors (like
^,$), lookarounds (like(?=...)), or backreferences (like\1).The returned parser supports parsing from a
Readerinput only if the regex has an upper bound in the match size (e.g.[a-z]{3}or(abc|d)). Regex patterns with unbounded match size (e.g.[a-z]+) will throwUnsupportedOperationExceptionwhen callingParser.parseToStream(Reader)orParser.probe(Reader), because Java regex requires the input to be fully loaded into memory, defeating the purpose of lazy loading fromReader- you might as well just explicitly load into aStringbefore parsing.The
patternstring is validated at compile-time by themug-errorprone(v10.9+) compiler plugin.If you need to extract values from capturing groups in the matched regex, use
regex(String, Function)or other group-mapping overloads (such asregex(String, BiFunction)) instead.NOTE that this method internally compiles the
patternso you should almost always pre-create and reuse the returnedParserobject instead of callingregex(pattern)in the inner loop or on-the-fly.- Throws:
IllegalArgumentException- if the pattern is invalid or contains forbidden features.- Since:
- 10.9
-
regex
public static <T> Parser<T> regex(@CompileTimeConstant String pattern, Function<? super String, ? extends T> mapper) Returns a leaf-level parser that matches the givenpatternand transforms the captured group value usingmapper.For example, to extract the area code from a phone number:
Parser<Integer> areaCode = regex("\\((\\d{3})\\) \\d{3}-\\d{4}", Integer::parseInt);Capturing groups start from group 1; group 0 (the top-level full match) is not passed to the
mapper. If you only need the top-level match, useregex(String); if you need both the entire match and nested groups, wrap the entire pattern in parentheses (e.g."((foo)(bar))"). Alternatively, calling.source()on the returned parser returns the full matched substring.Nested capturing groups are fully supported (ordered by opening parenthesis). Optional groups (such as
(?:-(\d+))?) and alternations (such as(\d+)|([a-z]+)) will passnullto themapperif that group was not matched.If named capturing groups (such as
"(?<name>...)") are used, their names are checked at compile time against lambda parameter names (or method reference parameters) with mug-errorprone 11.0+.- Throws:
IllegalArgumentException- if the regex does not have exactly 1 capturing group or is invalid / contains forbidden features.- Since:
- 11.0
-
regex
public static <T> Parser<T> regex(@CompileTimeConstant String pattern, BiFunction<? super String, ? super String, ? extends T> mapper) Returns a leaf-level parser that matches the givenpatternand transforms the 2 captured group values usingmapper.For example, to extract the area code and optional extension from a phone number:
Parser<PhoneNumber> phoneNumber = regex( "\\((?<areaCode>\\d{3})\\) \\d{3}-\\d{4}(?: x(?<extension>\\d+))?", (areaCode, extension) -> new PhoneNumber(areaCode, extension));Capturing groups start from group 1; group 0 (the top-level full match) is not passed to the
mapper. If you only need the top-level match, useregex(String); if you need both the entire match and nested groups, wrap the entire pattern in parentheses (e.g."((foo)(bar))"). Alternatively, calling.source()on the returned parser returns the full matched substring.Nested capturing groups are fully supported (ordered by opening parenthesis). Optional groups (such as
(?:-(\d+))?) and alternations (such as(\d+)|([a-z]+)) will passnullto themapperif that group was not matched.If named capturing groups (such as
"(?<name>...)") are used, their names are checked at compile time against lambda parameter names (or method reference parameters) with mug-errorprone 11.0+. For example, the following will fail compilation:Parser<PhoneNumber> phoneNumber = regex( "\\((?<areaCode>\\d{3})\\) \\d{3}-\\d{4}(?: x(?<extension>\\d+))?", (extension, areaCode) -> ...); // Compile error: parameters out of order- Throws:
IllegalArgumentException- if the regex does not have exactly 2 capturing groups or is invalid / contains forbidden features.- Since:
- 11.0
-
regex
public static <T> Parser<T> regex(@CompileTimeConstant String pattern, MapFrom3<? super String, ? extends T> mapper) Returns a leaf-level parser that matches the givenpatternand transforms the 3 captured group values usingmapper.Capturing groups start from group 1; group 0 (the top-level full match) is not passed to the
mapper. If you only need the top-level match, useregex(String); if you need both the entire match and nested groups, wrap the entire pattern in parentheses (e.g."((foo)(bar))"). Alternatively, calling.source()on the returned parser returns the full matched substring.Nested capturing groups are fully supported (ordered by opening parenthesis). Optional groups (such as
(?:-(\d+))?) and alternations (such as(\d+)|([a-z]+)) will passnullto themapperif that group was not matched.If named capturing groups (such as
"(?<name>...)") are used, their names are checked at compile time against lambda parameter names (or method reference parameters) with mug-errorprone 11.0+.- Throws:
IllegalArgumentException- if the regex does not have exactly 3 capturing groups or is invalid / contains forbidden features.- Since:
- 11.0
-
regex
public static <T> Parser<T> regex(@CompileTimeConstant String pattern, MapFrom4<? super String, ? extends T> mapper) Returns a leaf-level parser that matches the givenpatternand transforms the 4 captured group values usingmapper.Capturing groups start from group 1; group 0 (the top-level full match) is not passed to the
mapper. If you only need the top-level match, useregex(String); if you need both the entire match and nested groups, wrap the entire pattern in parentheses (e.g."((foo)(bar))"). Alternatively, calling.source()on the returned parser returns the full matched substring.Nested capturing groups are fully supported (ordered by opening parenthesis). Optional groups (such as
(?:-(\d+))?) and alternations (such as(\d+)|([a-z]+)) will passnullto themapperif that group was not matched.If named capturing groups (such as
"(?<name>...)") are used, their names are checked at compile time against lambda parameter names (or method reference parameters) with mug-errorprone 11.0+.- Throws:
IllegalArgumentException- if the regex does not have exactly 4 capturing groups or is invalid / contains forbidden features.- Since:
- 11.0
-
regex
public static <T> Parser<T> regex(@CompileTimeConstant String pattern, MapFrom5<? super String, ? extends T> mapper) Returns a leaf-level parser that matches the givenpatternand transforms the 5 captured group values usingmapper.Capturing groups start from group 1; group 0 (the top-level full match) is not passed to the
mapper. If you only need the top-level match, useregex(String); if you need both the entire match and nested groups, wrap the entire pattern in parentheses (e.g."((foo)(bar))"). Alternatively, calling.source()on the returned parser returns the full matched substring.Nested capturing groups are fully supported (ordered by opening parenthesis). Optional groups (such as
(?:-(\d+))?) and alternations (such as(\d+)|([a-z]+)) will passnullto themapperif that group was not matched.If named capturing groups (such as
"(?<name>...)") are used, their names are checked at compile time against lambda parameter names (or method reference parameters) with mug-errorprone 11.0+.- Throws:
IllegalArgumentException- if the regex does not have exactly 5 capturing groups or is invalid / contains forbidden features.- Since:
- 11.0
-
regex
public static <T> Parser<T> regex(@CompileTimeConstant String pattern, MapFrom6<? super String, ? extends T> mapper) Returns a leaf-level parser that matches the givenpatternand transforms the 6 captured group values usingmapper.Capturing groups start from group 1; group 0 (the top-level full match) is not passed to the
mapper. If you only need the top-level match, useregex(String); if you need both the entire match and nested groups, wrap the entire pattern in parentheses (e.g."((foo)(bar))"). Alternatively, calling.source()on the returned parser returns the full matched substring.Nested capturing groups are fully supported (ordered by opening parenthesis). Optional groups (such as
(?:-(\d+))?) and alternations (such as(\d+)|([a-z]+)) will passnullto themapperif that group was not matched.If named capturing groups (such as
"(?<name>...)") are used, their names are checked at compile time against lambda parameter names (or method reference parameters) with mug-errorprone 11.0+.- Throws:
IllegalArgumentException- if the regex does not have exactly 6 capturing groups or is invalid / contains forbidden features.- Since:
- 11.0
-
regex
public static <T> Parser<T> regex(@CompileTimeConstant String pattern, MapFrom7<? super String, ? extends T> mapper) Returns a leaf-level parser that matches the givenpatternand transforms the 7 captured group values usingmapper.Capturing groups start from group 1; group 0 (the top-level full match) is not passed to the
mapper. If you only need the top-level match, useregex(String); if you need both the entire match and nested groups, wrap the entire pattern in parentheses (e.g."((foo)(bar))"). Alternatively, calling.source()on the returned parser returns the full matched substring.Nested capturing groups are fully supported (ordered by opening parenthesis). Optional groups (such as
(?:-(\d+))?) and alternations (such as(\d+)|([a-z]+)) will passnullto themapperif that group was not matched.If named capturing groups (such as
"(?<name>...)") are used, their names are checked at compile time against lambda parameter names (or method reference parameters) with mug-errorprone 11.0+.- Throws:
IllegalArgumentException- if the regex does not have exactly 7 capturing groups or is invalid / contains forbidden features.- Since:
- 11.0
-
regex
public static <T> Parser<T> regex(@CompileTimeConstant String pattern, MapFrom8<? super String, ? extends T> mapper) Returns a leaf-level parser that matches the givenpatternand transforms the 8 captured group values usingmapper.Capturing groups start from group 1; group 0 (the top-level full match) is not passed to the
mapper. If you only need the top-level match, useregex(String); if you need both the entire match and nested groups, wrap the entire pattern in parentheses (e.g."((foo)(bar))"). Alternatively, calling.source()on the returned parser returns the full matched substring.Nested capturing groups are fully supported (ordered by opening parenthesis). Optional groups (such as
(?:-(\d+))?) and alternations (such as(\d+)|([a-z]+)) will passnullto themapperif that group was not matched.If named capturing groups (such as
"(?<name>...)") are used, their names are checked at compile time against lambda parameter names (or method reference parameters) with mug-errorprone 11.0+.- Throws:
IllegalArgumentException- if the regex does not have exactly 8 capturing groups or is invalid / contains forbidden features.- Since:
- 11.0
-