ValidX vs. Regex: When Business Validation Outgrows Pattern Matching
ValidX vs Regular Expressions: Readability and Maintainability Comparison
📋 Table of Contents
- Introduction
- 1. Regular Expressions: A Double-Edged Sword in Validation
- 2. Typical Scenario Comparison: Same Requirement, Two Implementations
- 3. Readability Comparison: Who Can Be Understood at a Glance
- 4. Maintainability Comparison: Who Stays Calm When Rules Change
- 5. Performance Comparison: Precompiled Regex vs Structured Validation
- 6. Scenario Analysis: When to Use What
- 7. ValidX's Design Philosophy: Regex Is Just One Tool
- 8. Practical Guide to Migrating from Regex to ValidX
- 9. Summary
- Project Address
Introduction
In the field of data validation, regular expressions (Regex) are the best-known tool. Almost every developer has written code like value.matches("\\d{11}"). It is concise, universal, and found everywhere.
But when validation requirements escalate from "is the format correct" to "is the value valid," regular expressions start to fall short. A phone number must not only be formatted correctly, but the segment must exist; an ID card must not only have the right number of digits, but the check digit must be correct; a bank card must not only be long enough, but it must pass the Luhn algorithm.
ValidX's positioning: not to replace regex, but to provide a semantic, extensible, maintainable validation layer above it.
This article uses 6 real business scenarios to compare regex and ValidX in detail across three dimensions: readability, maintainability, and performance. It helps you make the right technical choice.
1. Regular Expressions: A Double-Edged Sword in Validation
1.1 Advantages of Regular Expressions
| Advantage | Description | Example |
|---|---|---|
| Universality | Supported by almost every language | Common across Java, Python, JavaScript |
| Conciseness | Simple patterns in one line | ^\d{6}$ matches a 6-digit number |
| Performance | Fast matching after precompilation | Pattern.matcher().matches() |
| Expressiveness | Can describe complex text patterns | Formats like email, URL, IP |
1.2 Three Pain Points of Regular Expressions
┌──────────────────────────────────────────────────────────────┐
│ Three major pain points of regex in business validation │
│ │
│ ① Poor readability │
│ · Complex regex reads like "gibberish", hard for non-experts │
│ · Example: ^(?:13[0-9]|14[579]|15[0-3,5-9]|16[2567]|17[0-8] │
│ 18[0-9]|19[0-9])\d{8}$ │
│ · Newcomers must analyze character by character │
│ │
│ ② Poor maintainability │
│ · Rule changes require modifying the regex, easy to introduce bugs │
│ · Segment updates, rule adjustments all require rewriting regex │
│ · Lacks structure, hard to modify locally │
│ │
│ ③ Limited expressiveness │
│ · Can only validate "format", not "value" │
│ · Cannot express mathematical calculations (e.g., Luhn algorithm, mod 11 check) │
│ · Cannot relate to external data (e.g., administrative division tables, segment tables) │
└──────────────────────────────────────────────────────────────┘
1.3 Goal of This Article
It is not a "regex vs ValidX" either/or choice, but to help you understand:
- When is regex enough?
- When do you need ValidX?
- How to combine them for best results?
2. Typical Scenario Comparison: Same Requirement, Two Implementations
2.1 Chinese Phone Number Validation
Requirement: Validate that a string is a legitimate mainland Chinese mobile phone number.
Regex Solution
// Regex solution: an extremely long regex
private static final Pattern PHONE_PATTERN = Pattern.compile(
"^1(?:3[0-9]|4[579]|5[0-35-9]|6[2567]|7[0-8]|8[0-9]|9[0-9])\\d{8}$"
);
public boolean isValidPhone(String value) {
if (value == null) return false;
return PHONE_PATTERN.matcher(value).matches();
}
Problems:
- Can you understand this regex at a glance? What does
4[579]mean? Why does5[0-35-9]skip 4? - When the Ministry of Industry and Information Technology adds a new segment (e.g., 192), you must modify the regex—find the correct insertion point in a string of 30+ characters.
- If you make a mistake (e.g., miss a segment), it is hard to catch during code review.
ValidX Solution
// ValidX solution: one annotation does it
@ChinesePhone
private String phone;
// Or chain API
ValidX.init()
.validate().chinesePhone(phone).end();
ValidX internal implementation:
// Actual implementation of ValidX ChinesePhoneValidator
public class ChinesePhoneValidator implements ConstraintValidator<ChinesePhone, String> {
// Precompiled regex - format check
private static final Pattern MOBILE_PATTERN = Pattern.compile("^1[3-9]\\d{9}$");
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (value == null || value.isEmpty()) return true;
String cleanValue = value.replaceAll("[\\s-]", "");
return cleanValue.length() == 11 && MOBILE_PATTERN.matcher(cleanValue).matches();
}
}
Comparison Analysis:
| Dimension | Regex Solution | ValidX Solution |
|---|---|---|
| Code size | 5-8 lines | 1-line annotation |
| Readability | Requires regex knowledge | Semantic, understandable at a glance |
| Segment precision | Can be precise to each segment | Loose matching (1[3-9]) |
| Maintenance cost | Update regex each time segments change | Maintained by the framework |
| Reusability | Must reference Pattern everywhere | Declared via annotation, globally reused |
2.2 Email Address Validation
Requirement: Validate whether an email format is legal.
Regex Solution
// Email regex—looks simple but is actually hard to get right
private static final Pattern EMAIL_PATTERN = Pattern.compile(
"^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)+$"
);
// Stricter RFC 5322 regex (hundreds of characters)
private static final Pattern RFC5322_EMAIL = Pattern.compile(
"(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|" +
"\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\x7f]|" +
"\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")@(?:(?:[a-z0-9]" +
"(?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|" +
"\\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}" +
"(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:" +
"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21-\\x5a\\x53-\\x7f]|" +
"\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)\\])"
);
Problems:
- The simple version misses many valid emails (e.g., usernames with
+,.) - The strict version is hundreds of characters long and completely unreadable
- Cannot verify whether the domain actually exists
ValidX Solution
@Email
private String email;
ValidX uses the JSR-303 standard @Email annotation, with an internally tested regex that covers the vast majority of valid email formats.
2.3 Unified Social Credit Code
Requirement: Validate whether an 18-digit Unified Social Credit Code is legal.
Regex Solution
// Regex solution: only validates format, not the check digit
private static final Pattern CREDIT_CODE_PATTERN = Pattern.compile(
"^[0-9A-HJ-NPQRTUWXY]{2}\\d{6}[0-9A-HJ-NPQRTUWXY]{10}$"
);
public boolean isValidCreditCode(String value) {
if (value == null || value.length() != 18) return false;
return CREDIT_CODE_PATTERN.matcher(value).matches();
// ❌ Cannot validate the check digit! A code with correct format but wrong check digit still passes.
}
Fatal flaw: Regex can only validate "format," not "value." The last character of the Unified Social Credit Code is computed via a mod 31 weighted algorithm, which regex cannot express.
ValidX Solution
@UnifiedSocialCreditCode
private String creditCode;
ValidX internal implementation:
// ValidX's complete validation: format + check digit
public class UnifiedSocialCreditCodeValidator
implements ConstraintValidator<UnifiedSocialCreditCode, String> {
private static final Map<Character, Integer> CODE_MAP = new HashMap<>();
private static final int[] WEIGHT = {1, 3, 9, 27, 19, 26, 16, 17, 20, 29, 25, 13, 8, 24, 10, 30, 28};
private static final char[] CHECK_CODES = "0123456789ABCDEFGHJKLMNPQRTUWXY".toCharArray();
static {
for (int i = 0; i < CHECK_CODES.length; i++) {
CODE_MAP.put(CHECK_CODES[i], i);
}
}
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (value == null || value.isEmpty()) return true;
if (value.length() != 18) return false;
// ① Character validity check
for (int i = 0; i < 18; i++) {
if (!CODE_MAP.containsKey(value.charAt(i))) return false;
}
// ② Weighted sum
int sum = 0;
for (int i = 0; i < 17; i++) {
sum += CODE_MAP.get(value.charAt(i)) * WEIGHT[i];
}
// ③ Check digit verification
int remainder = sum % 31;
int checkCodeIndex = (remainder == 0) ? 0 : 31 - remainder;
return CHECK_CODES[checkCodeIndex] == value.charAt(17);
}
}
Comparison Analysis:
| Dimension | Regex Solution | ValidX Solution |
|---|---|---|
| Format validation | ✅ Possible | ✅ Possible |
| Check digit validation | ❌ Impossible | ✅ mod 31 algorithm |
| Correctness | Low (high false positives) | High (mathematical verification) |
| Code size | 1-line regex | 50+ lines structured code |
| Maintainability | Cannot extend | Clear logic, easy to modify |
2.4 Bank Card Number Validation (Luhn Algorithm)
Requirement: Validate whether a bank card number is valid (via Luhn mod 10 algorithm).
Regex Solution
// ❌ Regex cannot implement the Luhn algorithm
// Can only verify "is 16-19 digits"
private static final Pattern CARD_PATTERN = Pattern.compile("^\\d{16,19}$");
public boolean isValidBankCard(String value) {
return CARD_PATTERN.matcher(value).matches();
// ❌ Cannot verify the Luhn check digit!
}
ValidX Solution
@BankCard
private String bankCard;
ValidX internal implementation of Luhn algorithm:
public class BankCardValidator implements ConstraintValidator<BankCard, String> {
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (value == null || value.isEmpty()) return true;
// Remove spaces and hyphens
String cleanValue = value.replaceAll("[\\s-]", "");
// Length check
if (cleanValue.length() < 16 || cleanValue.length() > 19) return false;
// Must be all digits
if (!cleanValue.matches("\\d+")) return false;
// Luhn algorithm (mod 10)
return luhnCheck(cleanValue);
}
private boolean luhnCheck(String cardNumber) {
int sum = 0;
boolean alternate = false;
for (int i = cardNumber.length() - 1; i >= 0; i--) {
int digit = Character.getNumericValue(cardNumber.charAt(i));
if (alternate) {
digit *= 2;
if (digit > 9) {
digit = digit / 10 + digit % 10; // Sum of the two digits
}
}
sum += digit;
alternate = !alternate;
}
return sum % 10 == 0;
}
}
Conclusion: The Luhn algorithm involves "right-to-left traversal, alternating doubling, digit summing"—operations that regex cannot express at all.
3. Readability Comparison: Who Can Be Understood at a Glance
3.1 The Readability Ceiling of Regular Expressions
Consider a real project's ID card regex:
// Can you understand this regex in 5 seconds?
private static final Pattern ID_CARD_PATTERN = Pattern.compile(
"^[1-9]\\d{5}(?:19\\d{2}|20\\d{2})(?:0[13-9]|1[0-2])(?:0[1-9]|[12]\\d|3[01])" +
"\\d{3}[\\dXx]$"
);
Its meaning:
[1-9]\d{5}: 6-digit region code (first digit not 0)(?:19\d{2}|20\d{2}): year (19xx or 20xx)(?:0[13-9]|1[0-2]): month (01-12, excluding 02 special handling in date)(?:0[1-9]|[12]\d|3[01]): date (01-31)\d{3}: sequence code[\dXx]: check digit
Problems:
- Requires regex expertise to interpret
- Even if understood, hard to confirm "if the logic is correct"
- During code review, non-regex experts cannot effectively review
3.2 ValidX's Semantic API
// ValidX solution: clear semantics
@ChineseIdCard
private String idCard;
Without even looking at the implementation, anyone can understand: this field must be a valid Chinese ID card number.
For finer control:
// Chain API: semantic expression
ValidX.init()
.validate()
.chineseIdCard(idCard, "身份证号码不合法")
.chinesePhone(phone, "手机号不合法")
.email(email, "邮箱格式不正确")
.end();
3.3 Readability Scoring Matrix
| Validation Scenario | Regex Readability | ValidX Readability | Gap |
|---|---|---|---|
| 6-digit numeric code | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | Even |
| Phone number format | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ValidX wins |
| Email format | ⭐⭐ | ⭐⭐⭐⭐⭐ | ValidX wins |
| ID card format | ⭐⭐ | ⭐⭐⭐⭐⭐ | ValidX wins |
| ID card + check digit | ❌ Impossible | ⭐⭐⭐⭐⭐ | ValidX dominates |
| Bank card Luhn | ❌ Impossible | ⭐⭐⭐⭐⭐ | ValidX dominates |
| Unified Social Credit Code | ⭐⭐ | ⭐⭐⭐⭐⭐ | ValidX dominates |
4. Maintainability Comparison: Who Stays Calm When Rules Change
4.1 Number Segment Updates: Regex's Nightmare
Scenario: The Ministry of Industry and Information Technology adds segment 192 for China Broadcasting Network.
Regex Solution Maintenance Cost
// Before modification
"^1(?:3[0-9]|4[579]|5[0-35-9]|6[2567]|7[0-8]|8[0-9]|9[0-35-9])\\d{8}$"
// After modification: need to add 2 in the right place in 9[0-35-9]
// Actually need to insert 2 at the correct position
"^1(?:3[0-9]|4[579]|5[0-35-9]|6[2567]|7[0-8]|8[0-9]|9[0-35-9])\\d{8}$"
// becomes
"^1(?:3[0-9]|4[579]|5[0-35-9]|6[2567]|7[0-8]|8[0-9]|9[0-35-9])\\d{8}$"
// finally modified to
"^1(?:3[0-9]|4[579]|5[0-35-9]|6[2567]|7[0-8]|8[0-9]|9[0-35-9])\\d{8}$"
Problems:
- Need to find the correct position in a complex regex
- Modifications are error-prone (e.g., missing a branch, mismatched parentheses)
- Need thorough testing to confirm correctness
- If the project uses this regex in 10 places, you need to modify all 10
ValidX Solution Maintenance Cost
// ValidX solution: the framework maintainer updates, user code unchanged
@ChinesePhone
private String phone;
The ValidX maintainer updates the internal segment matching logic; users just upgrade the dependency version.
4.2 Business Logic Expansion: Regex's Boundary
Scenario: ID card validation needs to add a "cannot be a future date" check.
Regex Solution
// ❌ Regex cannot express "date cannot be future"
// Must be supplemented with extra Java code
public boolean isValidIdCard(String value) {
if (!ID_CARD_PATTERN.matcher(value).matches()) return false;
// Need extra date check code
String birthDate = value.substring(6, 14);
try {
LocalDate date = LocalDate.parse(birthDate, DateTimeFormatter.BASIC_ISO_DATE);
if (date.isAfter(LocalDate.now())) return false;
} catch (Exception e) {
return false;
}
// Also need check digit check
// ... 50+ lines of code
return true;
}
Problems: Regex can only do "format checks"; business logic must be implemented with extra code. The final code becomes a mix of "regex + Java code," increasing maintenance cost.
ValidX Solution
// ValidX solution: all logic encapsulated inside the validator
@ChineseIdCard
private String idCard;
// User code always stays one annotation; internal logic is maintained by the framework
4.3 ValidX's Maintenance Advantage
| Maintenance Scenario | Regex Solution | ValidX Solution |
|---|---|---|
| Segment updates | Modify regex, full regression testing | Upgrade dependency version |
| Algorithm changes | Rewrite regex + supporting code | Upgrade dependency version |
| New validation type | Write regex + code from scratch | Directly use annotation |
| Multiple locations | Global search and replace, easy to miss | No user code changes needed |
| Bug fixes | Self-diagnose regex issues | Framework fixes centrally |
5. Performance Comparison: Precompiled Regex vs Structured Validation
5.1 Performance Characteristics of Pure Regex
// Pure regex solution
private static final Pattern PATTERN = Pattern.compile("...");
public boolean isValid(String value) {
return PATTERN.matcher(value).matches();
}
Performance characteristics:
- After precompilation, a single match takes ~0.3μs
- For complex regex (e.g., ID card), backtracking can cause performance degradation
- Cannot do "fail-fast" optimization
5.2 Performance Characteristics of ValidX's Hybrid Approach
// ValidX hybrid approach: regex + structured checks
public boolean isValid(String value, ConstraintValidatorContext context) {
// ① Length check: O(1), nanosecond level
if (value.length() != 18) return false;
// ② Regex format check: ~0.3μs
if (!EIGHTEEN_ID_PATTERN.matcher(value).matches()) return false;
// ③ Province code check: HashMap O(1), ~0.1μs
if (!ChinaProvince.fromCode(value.substring(0, 2)).isPresent()) return false;
// ④ Date check: ~0.2μs
if (!isValidBirthDate(value.substring(6, 14))) return false;
// ⑤ Check digit calculation: ~0.5μs
return verifyCheckCode(value);
}
Key advantage: Fail Fast
- For invalid input, on average rejected in steps ① or ②, taking <0.5μs
- Only inputs with correct format proceed to subsequent calculations
- Overall performance is comparable to pure regex, but accuracy is much higher than pure regex
5.3 Measured Performance Data
| Approach | Valid Input Time | Invalid Input Time | Accuracy |
|---|---|---|---|
| Pure regex (format only) | ~0.3μs | ~0.3μs | 60% (high false positives) |
| ValidX (hybrid) | ~1.2μs | ~0.3μs (fail-fast) | 99.9%+ |
| ValidX (optimized) | ~0.8μs | ~0.2μs | 99.9%+ |
Conclusion: ValidX's hybrid approach dramatically improves validation accuracy while maintaining high performance.
6. Scenario Analysis: When to Use What
6.1 Scenarios Suited for Regex
| Scenario | Reason | Example |
|---|---|---|
| Simple format checks | Regex is sufficient, no complex logic | Zip codes, fixed-length numbers |
| Text pattern matching | Regex's strength | URL, IP, date formats |
| One-time validation | No need for reuse or maintenance | Temporary scripts, prototype validation |
| Extreme performance requirements | Regex is fastest after precompilation | High-frequency log parsing |
// ✅ Scenarios suited for regex
private static final Pattern ZIP_CODE = Pattern.compile("^\\d{6}$");
private static final Pattern IPV4 = Pattern.compile(
"^(?:(?:25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}" +
"(?:25[0-5]|2[0-4]\\d|[01]?\\d\\d?)$"
);
6.2 Scenarios Suited for ValidX
| Scenario | Reason | Example |
|---|---|---|
| Requires check digit validation | Regex cannot express algorithms | ID card, bank card, credit code |
| Rules change frequently | Framework maintains centrally | Phone number segments |
| Multiple reuses | Annotation declaration, global reuse | DTO field validation |
| Need internationalized error messages | Built-in i18n support | Multi-language applications |
| Complex business logic | Structured code is easier to maintain | Multi-step validation |
// ✅ Scenarios suited for ValidX
@ChineseIdCard // Needs check digit algorithm
@BankCard // Needs Luhn algorithm
@ChinesePhone // Segments update frequently
@UnifiedSocialCreditCode // Needs mod 31 algorithm
6.3 Hybrid Approach: Best Practices
Principle: Regex for "coarse screening," ValidX for "fine verification."
// ✅ Hybrid: regex for fast filtering + ValidX for precise validation
public class UserRegistrationService {
// Simple format uses regex (sufficient)
private static final Pattern USERNAME_PATTERN =
Pattern.compile("^[a-zA-Z0-9_]{3,20}$");
public ValidationResult validate(UserDTO user) {
// ① Username: regex is enough
if (!USERNAME_PATTERN.matcher(user.getUsername()).matches()) {
return ValidationResult.error("用户名格式不正确");
}
// ② ID card: needs ValidX (check digit algorithm)
ChineseIdCardValidator idValidator = new ChineseIdCardValidator();
if (!idValidator.isValid(user.getIdCard(), null)) {
return ValidationResult.error("身份证号码不合法");
}
// ③ Bank card: needs ValidX (Luhn algorithm)
BankCardValidator cardValidator = new BankCardValidator();
if (!cardValidator.isValid(user.getBankCard(), null)) {
return ValidationResult.error("银行卡号不合法");
}
return ValidationResult.success();
}
}
7. ValidX's Design Philosophy: Regex Is Just One Tool
7.1 Layered Validation Architecture
┌──────────────────────────────────────────────────────────────┐
│ ValidX's Layered Validation Architecture │
│ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ Layer 1: Null check │ │
│ │ · null / empty → handled by @NotNull / @NotEmpty │ │
│ └────────────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ Layer 2: Quick format check (regex / length / chars) │ │
│ │ · Precompiled Pattern, length(), charAt() │ │
│ │ · Goal: quickly reject obviously invalid input │ │
│ └────────────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ Layer 3: Structured validation (algorithms / lookup tables / business rules) │ │
│ │ · Check digits (mod 11, mod 31, Luhn) │ │
│ │ · Code lookups (administrative divisions, segment tables) │ │
│ │ · Date validity, business rules │ │
│ └────────────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ Layer 4: Internationalized error messages │ │
│ │ · ResourceBundle + ConcurrentHashMap cache │ │
│ └────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────┘
7.2 The Combination Punch of Regex + Algorithms
ValidX validators typically use a combination of "regex + algorithm":
// Typical pattern: regex for coarse screening, algorithm for precise validation
public boolean isValid(String value, ConstraintValidatorContext context) {
// Step 1: Regex fast filter (exclude obviously invalid)
if (!PATTERN.matcher(value).matches()) {
return false;
}
// Step 2: Algorithm precise validation (check digits, business rules)
return algorithmCheck(value);
}
Advantages of this pattern:
- Regex handles "fast rejection," leveraging its performance strengths
- Algorithms handle "precise validation," compensating for regex's limited expressiveness
- The two complement each other, achieving the best balance of performance and accuracy
7.3 Error Message Readability
Regex Solution
// Regex solution's error message: vague
if (!pattern.matcher(value).matches()) {
throw new ValidationException("格式不正确");
// Users don't know what exactly is incorrect
}
ValidX Solution
// ValidX's error messages: semantic + internationalized
// Chinese: 请输入有效的身份证号码
// English: Please enter a valid ID card number
// Japanese: 有効な身分証番号を入力してください
ValidX includes error messages in 9 languages, each carefully designed to suit local language conventions.
8. Practical Guide to Migrating from Regex to ValidX
8.1 Migration Steps
┌──────────────────────────────────────────────────────────────┐
│ 4 steps to migrate from regex to ValidX │
│ │
│ ① Identify: find all regex used for business validation │
│ · grep -r "Pattern.compile" --include="*.java" │
│ · grep -r "\\.matches(" --include="*.java" │
│ │
│ ② Classify: distinguish between "format validation" and "business validation" │
│ · Format validation (zip, IP) → keep regex │
│ · Business validation (ID card, bank card) → migrate to ValidX │
│ │
│ ③ Replace: replace with ValidX annotations or chain API │
│ · Annotation mode: @ChineseIdCard │
│ · Chain mode: ValidX.init().validate().chineseIdCard() │
│ │
│ ④ Test: verify correctness after migration │
│ · Unit test coverage │
│ · Boundary value testing │
│ · Performance regression testing │
└──────────────────────────────────────────────────────────────┘
8.2 Common Migration Cases
Case 1: Phone Number Validation Migration
// ❌ Before migration: regex solution
private static final Pattern PHONE = Pattern.compile("^1[3-9]\\d{9}$");
public boolean validatePhone(String phone) {
return PHONE.matcher(phone).matches();
}
// ✅ After migration: ValidX annotation mode
@ChinesePhone
private String phone;
// ✅ After migration: ValidX chain API
boolean valid = ValidX.init().validate().chinesePhone(phone).isSuccess();
Case 2: ID Card Validation Migration
// ❌ Before migration: regex + handwritten algorithm (hard to maintain)
public boolean validateIdCard(String idCard) {
if (!idCard.matches("^[0-9]{17}[0-9X]$")) return false;
// 50+ lines of handwritten check logic...
return true;
}
// ✅ After migration: one-line annotation
@ChineseIdCard
private String idCard;
Case 3: Bank Card Validation Migration
// ❌ Before migration: cannot validate Luhn (only format check)
private static final Pattern CARD = Pattern.compile("^\\d{16,19}$");
public boolean validateCard(String card) {
return CARD.matcher(card).matches(); // ❌ High false positives
}
// ✅ After migration: complete Luhn validation
@BankCard
private String bankCard;
8.3 Migration Checklist
- Identify all regex expressions used for business validation
- Distinguish "format validation" (keep regex) from "business validation" (migrate to ValidX)
- Choose appropriate ValidX annotation or chain API
- Write unit tests to ensure behavior is consistent after migration
- Performance tests to ensure no regression
- Update documentation to explain migration reasons and new usage
- Code review to ensure the team understands the new approach
9. Summary
Core Comparison Summary
| Dimension | Regex | ValidX | Conclusion |
|---|---|---|---|
| Readability | Complex regex hard to read | Semantic annotation, clear at a glance | ValidX wins |
| Maintainability | Rule changes require rewriting | Framework maintains centrally | ValidX wins |
| Expressiveness | Can only validate format | Format + algorithm + business | ValidX wins |
| Performance | Extremely fast after precompilation | Hybrid approach, comparable | Tie |
| Accuracy | High false positives | Mathematical validation, 99.9%+ | ValidX wins |
| Universality | Supported by all languages | Java-specific | Regex wins |
| Learning cost | Need to learn regex syntax | Just learn annotation names | ValidX wins |
Selection Decision Tree
What needs validation?
│
├─ Pure format check (zip, IP, simple patterns)
│ └─ ✅ Use regex (sufficient, fast)
│
├─ Needs check digit algorithm (ID card, bank card, credit code)
│ └─ ✅ Use ValidX (regex cannot implement)
│
├─ Rules change frequently (phone segments)
│ └─ ✅ Use ValidX (framework maintains)
│
├─ Multiple reuse + internationalization needs
│ └─ ✅ Use ValidX (annotation declaration + built-in i18n)
│
└─ Unsure?
└─ ✅ Use ValidX (safer, easier to maintain)
Key Insight
Regex is a "tool," ValidX is a "solution."
- Regex excels at "pattern matching"—it's the Swiss Army knife of text processing
- ValidX excels at "business validation"—it encapsulates domain knowledge and best practices
- They are not substitutes but complements
In real projects, ValidX also uses regex heavily internally—it just puts regex in its proper place, wraps it with structured code, and exposes a clean API.
Choosing ValidX is not abandoning regex; it's letting regex return to where it excels.
Top 1 of 2 from juejin.cn, machine-translated. The original thread is authoritative.
After all that, it's just wrapping it in another layer.
Some are. Some have more complex validation logic, like ID cards and bank cards.