Safe string parsing methods that return Option types instead of throwing exceptions. These methods provide consistent parsing across all numeric types and handle edge cases gracefully.
Extension methods for String that safely parse to numeric types, returning None for invalid input instead of throwing exceptions.
implicit class StringOps(s: String) {
/**
* Parse string to Boolean safely
* Accepts "true"/"false" (case-insensitive)
* @return Some(boolean) if valid, None otherwise
*/
def toBooleanOption: Option[Boolean]
/**
* Parse string to Byte safely
* @return Some(byte) if valid and within Byte range, None otherwise
*/
def toByteOption: Option[Byte]
/**
* Parse string to Short safely
* @return Some(short) if valid and within Short range, None otherwise
*/
def toShortOption: Option[Short]
/**
* Parse string to Int safely
* @return Some(int) if valid and within Int range, None otherwise
*/
def toIntOption: Option[Int]
/**
* Parse string to Long safely
* @return Some(long) if valid and within Long range, None otherwise
*/
def toLongOption: Option[Long]
/**
* Parse string to Float safely
* Handles special values like "NaN", "Infinity", "-Infinity"
* @return Some(float) if valid, None otherwise
*/
def toFloatOption: Option[Float]
/**
* Parse string to Double safely
* Handles special values like "NaN", "Infinity", "-Infinity"
* @return Some(double) if valid, None otherwise
*/
def toDoubleOption: Option[Double]
}Usage Examples:
import scala.collection.compat._
// Integer parsing
"42".toIntOption // Some(42)
"abc".toIntOption // None
"2147483648".toIntOption // None (exceeds Int.MaxValue)
"".toIntOption // None
" 123 ".toIntOption // Some(123) - whitespace trimmed
// Boolean parsing
"true".toBooleanOption // Some(true)
"TRUE".toBooleanOption // Some(true)
"false".toBooleanOption // Some(false)
"FALSE".toBooleanOption // Some(false)
"yes".toBooleanOption // None
"1".toBooleanOption // None
// Floating point parsing
"3.14".toDoubleOption // Some(3.14)
"NaN".toFloatOption // Some(Float.NaN)
"Infinity".toDoubleOption // Some(Double.PositiveInfinity)
"-Infinity".toFloatOption // Some(Float.NegativeInfinity)
"invalid".toDoubleOption // None
// Byte range validation
"127".toByteOption // Some(127)
"128".toByteOption // None (exceeds Byte.MaxValue)
"-128".toByteOption // Some(-128)
"-129".toByteOption // None (below Byte.MinValue)
// Long parsing with large numbers
"9223372036854775807".toLongOption // Some(Long.MaxValue)
"9223372036854775808".toLongOption // None (exceeds Long.MaxValue)Common patterns for using safe parsing methods in real-world applications.
Configuration Reading:
import scala.collection.compat._
def readConfig(properties: Map[String, String]): Config = {
Config(
port = properties.get("server.port").flatMap(_.toIntOption).getOrElse(8080),
timeout = properties.get("timeout.seconds").flatMap(_.toLongOption).getOrElse(30L),
debugEnabled = properties.get("debug.enabled").flatMap(_.toBooleanOption).getOrElse(false),
maxMemory = properties.get("max.memory.gb").flatMap(_.toDoubleOption).getOrElse(2.0)
)
}CSV Parsing:
import scala.collection.compat._
def parseCsvRow(row: String): Option[Person] = {
val fields = row.split(",")
if (fields.length >= 3) {
for {
name <- Option(fields(0)).filter(_.nonEmpty)
age <- fields(1).toIntOption
salary <- fields(2).toDoubleOption
} yield Person(name, age, salary)
} else None
}
// Usage
val csvData = List(
"Alice,25,50000.0",
"Bob,30,60000.0",
"Charlie,invalid,70000.0", // Will be None due to invalid age
"Diana,28,80000.0"
)
val people = csvData.flatMap(parseCsvRow)JSON-like String Processing:
import scala.collection.compat._
def parseJsonValue(value: String): Option[JsonValue] = {
value.trim match {
case s if s.startsWith("\"") && s.endsWith("\"") =>
Some(JsonString(s.slice(1, s.length - 1)))
case s =>
s.toIntOption.map(JsonInt(_))
.orElse(s.toDoubleOption.map(JsonDouble(_)))
.orElse(s.toBooleanOption.map(JsonBoolean(_)))
}
}Input Validation:
import scala.collection.compat._
case class ValidationError(field: String, message: String)
def validateUserInput(input: Map[String, String]): Either[List[ValidationError], User] = {
val errors = List.newBuilder[ValidationError]
val name = input.get("name") match {
case Some(n) if n.nonEmpty => Some(n)
case _ =>
errors += ValidationError("name", "Name is required")
None
}
val age = input.get("age").flatMap(_.toIntOption) match {
case Some(a) if a >= 0 && a <= 150 => Some(a)
case Some(_) =>
errors += ValidationError("age", "Age must be between 0 and 150")
None
case None =>
errors += ValidationError("age", "Valid age is required")
None
}
val email = input.get("email") match {
case Some(e) if e.contains("@") => Some(e)
case _ =>
errors += ValidationError("email", "Valid email is required")
None
}
val errorList = errors.result()
if (errorList.isEmpty) {
Right(User(name.get, age.get, email.get))
} else {
Left(errorList)
}
}Numeric Range Validation:
import scala.collection.compat._
def parsePort(portStr: String): Option[Int] = {
portStr.toIntOption.filter(port => port > 0 && port <= 65535)
}
def parsePercentage(percentStr: String): Option[Double] = {
percentStr.toDoubleOption.filter(pct => pct >= 0.0 && pct <= 100.0)
}
def parseTimeout(timeoutStr: String): Option[Long] = {
timeoutStr.toLongOption.filter(_ > 0)
}
// Usage with validation
def configureServer(config: Map[String, String]): Either[String, ServerConfig] = {
for {
port <- config.get("port")
.flatMap(parsePort)
.toRight("Invalid port number")
timeout <- config.get("timeout")
.flatMap(parseTimeout)
.toRight("Invalid timeout value")
successRate <- config.get("success.rate")
.flatMap(parsePercentage)
.toRight("Invalid success rate percentage")
} yield ServerConfig(port, timeout, successRate)
}Combining with Validation:
import scala.collection.compat._
// Combine multiple optional parses
def parseCoordinate(x: String, y: String): Option[(Double, Double)] = {
for {
xVal <- x.toDoubleOption
yVal <- y.toDoubleOption
} yield (xVal, yVal)
}
// Parse with default values
def parseWithDefaults(config: Map[String, String]): AppConfig = {
AppConfig(
threads = config.get("threads").flatMap(_.toIntOption).getOrElse(4),
memory = config.get("memory").flatMap(_.toLongOption).getOrElse(1024L),
ratio = config.get("ratio").flatMap(_.toDoubleOption).getOrElse(0.75)
)
}
// Parse with validation chains
def parsePositiveInt(s: String): Option[Int] = {
s.toIntOption.filter(_ > 0)
}
def parseNonEmptyString(s: String): Option[String] = {
Option(s).filter(_.trim.nonEmpty)
}The string parsing methods handle various edge cases:
These parsing methods provide a safe, functional approach to string conversion that integrates well with Scala's Option-based error handling patterns.