DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Related

  • Testcontainers With Kotlin and Spring Data R2DBC
  • Why Round-Robin Won't Save You: Load Balancing Challenges in Data Streaming Services With Heterogeneous Traffic
  • Beyond Partitioning and Z-Order: A Deep Dive into Liquid Clustering for Unity Catalog Managed Tables
  • Inside What Actually Breaks in Large-Scale S/4HANA Conversions (And How to Prevent It)

Trending

  • The 7 Pillars of Meeting Design: Transforming Expensive Conversations into Decision Assets
  • The Hidden Cost of AI Tokens: Engineering Patterns for 10x Resource Efficiency
  • Migrate a Hardcoded LangGraph Agent to LaunchDarkly AI Configs in 20 Minutes
  • The Repo Tracker: Automating My Daily GitHub Catch-Up
  1. DZone
  2. Data Engineering
  3. Data
  4. DSL Validations: Child Properties

DSL Validations: Child Properties

After learning how to validate simple properties on a class, the DSL is extended to allow accessing properties on sub-objects contained within the top-level class.

By 
Scott Sosna user avatar
Scott Sosna
DZone Core CORE ·
Mar. 28, 24 · Tutorial
Likes (1)
Comment
Save
Tweet
Share
1.8K Views

Join the DZone community and get the full member experience.

Join For Free

This is part 2 of a 4-part tutorial

  • Part 1: DSL Validations: Properties
  • Part 2: DSL Validations: Child Properties
  • Part 3: DSL Validations: Operators
  • Part 4: DSL Validations: The Whole Enchilada

Part 1 introduced the concept of property validators, providing the building blocks for DSL validations: access an object's property and check its value.

However, property validators are limited to simple data types. Specifically, how do you validate a property on an object contained by the base object? That's the purpose of ChildPropertyValidator validators.

ChildPropertyValidator

The ChildPropertyValidator is a special-case PropertyValidator which accesses a property which itself is an object — contained within the base object — and applies a PropertyValidator on its property.

  • propertyName is informational only, used when creating a violation when validation fails;
  • getter is the function that returns the object property. As with a generic property validator, the generic <S> defines the class on which the getter is called and <T> identifies the return data type of the getter, the class of the contained object;
  • child is the property validator for a property on the contained object.

When the property of the contained object is not null, the property validator provided is executed against that contained object; when the contained object is null, validation fails, and a ConstraintViolation is created.

Kotlin
 
class ChildPropertyValidator<S,T> (propertyName: String,
                                   getter: S.() -> T?,
                                   val child: PropertyValidator<T>)
    : AbstractPropertyValidator<T, S>(propertyName, getter) {

    override fun validate(source: S,
                          errors: MutableSet<ConstraintViolation<S>>)
        : Boolean {

        //  Attempt to get the subdocument
        val childSource = getter.invoke(source)

        //  If subdocument is not-null validate child document; otherwise
        //  generate error and return
        return if (childSource != null) {
            validateChild(source, childSource, errors)
        } else {
            errors.add(
                createViolation(source,
                    ERROR_MESSAGE.format(propertyName),
                    ERROR_MESSAGE,
                    propertyName,
                    null))
            false
        }
    }

    private fun validateChild (source: S, 
                               childSource: T, 
                               errors: MutableSet<ConstraintViolation<S>>)
        : Boolean {
        
        val set = mutableSetOf<ConstraintViolation<T>>()
        val success = child.validate(childSource, set)

        //  Validator interface limits errors to single type, therefore need to recast the error as the root type rather
        //  than the child type/source on which we were validated.  Stinks, but ConstraintViolation<*> cause other problems
        if (!success) {
            val error = set.first()
            errors.add(
                createViolation(source,
                    error.message,
                    error.messageTemplate,
                    propertyName,
                    error.invalidValue))
        }

        return success
    }

    companion object {
        private const val ERROR_MESSAGE = "%s is required for evaluating."
    }
}


Putting It All Together

Let's define a simple Kotlin data class that defines a (very) basic Student:

Kotlin
 
data class Address(
   val line1: String?,
   val line2: String?
   val city: String,
   val state: String,
   val zipCode: String
)

data class Student(
   val studentId: String,
   val firstName: String?,
   val lastName: String?,
   val emailAddress: String?,
   val localAddress: Address
)


In this example, we need to validate that the student's address has a correctly-formatted United States zip code: five digits (i.e., 12345, most common) or five digits/hyphen/four digits (i.e., 12345-6789, Zip+4).  The ZipCodeFormatValidator is the property validator that checks for either of these two formats.

The sample code demonstrates how the ZipCodeFormatValidator is wrapped by a ChildPropertyValidator to validate the zip code within the contained Address object.

Kotlin
 
// Assume the student is created from a database entry
val myStudent = retrieveStudent("studentId")

// Create instance of property validator
val zipValidator = ZipCodeFormatValidator("address",
                                          Address::zipCode)

// Create child property validator for the Student
val childValidator = ChildPropertyValidator("address.zipCode",
                                            Student::address,
                                            zipValidator)

// Validate the property
val violations = mutableSetOf<ConstraintViolation<T>>()
childValidator.validate(myStudent, violations)

// empty collection means successful validation
val successfullyValidated = violations.isEmpty()


CAVEAT EMPTOR: ChildPropertyValidator is itself a PropertyValidator and therefore it's possible to navigate multiple levels deep; however, the readability and latency likely suffer. Weigh the trade-offs of a custom class-level validation versus implementing via the DSL.

Final Comments

While seemingly benign, ChildPropertyValidators are a necessity for building DSL validations for anything but the most simple class definitions. In Part 3, we'll demonstrate how to combine multiple validators to do more complex class-level validations without the need to write code.

Data (computing) Kotlin (programming language) Data Types Domain-Specific Language

Published at DZone with permission of Scott Sosna. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Testcontainers With Kotlin and Spring Data R2DBC
  • Why Round-Robin Won't Save You: Load Balancing Challenges in Data Streaming Services With Heterogeneous Traffic
  • Beyond Partitioning and Z-Order: A Deep Dive into Liquid Clustering for Unity Catalog Managed Tables
  • Inside What Actually Breaks in Large-Scale S/4HANA Conversions (And How to Prevent It)

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

  • RSS
  • X
  • Facebook

ABOUT US

  • About DZone
  • Support and feedback
  • Community research

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 215
  • Nashville, TN 37211
  • [email protected]

Let's be friends:

  • RSS
  • X
  • Facebook