
Type-safe, reflection-free diffing of serializable data structures, performing deep comparisons of nested objects, lists, and maps; supports compile-time serializers, customizable list strategies and formatted statements.
A diffing library for Kotlin/Multiplatform which uses kotlinx.serialization for performant, type-safe diffing without reflection.
kotlinx.serialization's compile-time generated serializers.Add the dependency to your build.gradle.kts (check Maven Central for the latest version):
implementation("com.jessecorbett:kotlinx-serialization-diff:1.1.0")Use the diff function to get a list of changes:
import kotlinx.serialization.diff.diff
import kotlinx.serialization.Serializable
@Serializable
data class User(val name: String, val age: Int)
val user1 = User("Alice", 30)
val user2 = User("Alice", 31)
val changes = diff(user1, user2)
// Changes: [PropertyValueChanged(path=[Property(name=age)], left=30, right=31, ...)]For logging or UI display, use diffStatements to get a pre-formatted multi-line string:
import kotlinx.serialization.diff.diffStatements
val statements = diffStatements(user1, user2)
println(statements)
// Output: Property 'age' changed from '30' to '31'Alternatively, call .format() on any List<Diff>:
import kotlinx.serialization.diff.format
val output = diff(user1, user2).format()You can customize the diffing behavior by passing a DiffConfig.
kotlinx-serialization-diff supports two strategies for diffing lists and collections:
INDEX_BY_INDEX (Default): Compares elements at the same index. Best for fixed-position data.LCS: Uses the Longest Common Subsequence (Myers' diff algorithm) to identify insertions and deletions. Best for lists where items might be shifted.import kotlinx.serialization.diff.DiffConfig
import kotlinx.serialization.diff.ListDiffStrategy
val config = DiffConfig(listStrategy = ListDiffStrategy.LCS)
val changes = diff(oldList, newList, config)If your models use contextual or polymorphic serialization that requires a custom SerializersModule, you can provide it in the configuration:
import kotlinx.serialization.modules.SerializersModule
val myModule = SerializersModule {
contextual(MyCustomSerializer)
}
val config = DiffConfig(serializersModule = myModule)
val changes = diff(obj1, obj2, config)The library identifies several types of changes:
PropertyValueChanged: A primitive value (String, Int, Boolean, etc.) or null changed.FieldAdded / FieldRemoved: A property was added or removed (e.g., when diffing polymorphic types).ElementAdded / ElementRemoved: An element was added to or removed from a List or Set.MapEntryAdded / MapEntryRemoved: A key-value pair was added to or removed from a Map.TypeMismatch: The structure changed significantly (e.g., a field changed from a primitive to a nested object).This project is licensed under the Apache License, Version 2.0.
A diffing library for Kotlin/Multiplatform which uses kotlinx.serialization for performant, type-safe diffing without reflection.
kotlinx.serialization's compile-time generated serializers.Add the dependency to your build.gradle.kts (check Maven Central for the latest version):
implementation("com.jessecorbett:kotlinx-serialization-diff:1.1.0")Use the diff function to get a list of changes:
import kotlinx.serialization.diff.diff
import kotlinx.serialization.Serializable
@Serializable
data class User(val name: String, val age: Int)
val user1 = User("Alice", 30)
val user2 = User("Alice", 31)
val changes = diff(user1, user2)
// Changes: [PropertyValueChanged(path=[Property(name=age)], left=30, right=31, ...)]For logging or UI display, use diffStatements to get a pre-formatted multi-line string:
import kotlinx.serialization.diff.diffStatements
val statements = diffStatements(user1, user2)
println(statements)
// Output: Property 'age' changed from '30' to '31'Alternatively, call .format() on any List<Diff>:
import kotlinx.serialization.diff.format
val output = diff(user1, user2).format()You can customize the diffing behavior by passing a DiffConfig.
kotlinx-serialization-diff supports two strategies for diffing lists and collections:
INDEX_BY_INDEX (Default): Compares elements at the same index. Best for fixed-position data.LCS: Uses the Longest Common Subsequence (Myers' diff algorithm) to identify insertions and deletions. Best for lists where items might be shifted.import kotlinx.serialization.diff.DiffConfig
import kotlinx.serialization.diff.ListDiffStrategy
val config = DiffConfig(listStrategy = ListDiffStrategy.LCS)
val changes = diff(oldList, newList, config)If your models use contextual or polymorphic serialization that requires a custom SerializersModule, you can provide it in the configuration:
import kotlinx.serialization.modules.SerializersModule
val myModule = SerializersModule {
contextual(MyCustomSerializer)
}
val config = DiffConfig(serializersModule = myModule)
val changes = diff(obj1, obj2, config)The library identifies several types of changes:
PropertyValueChanged: A primitive value (String, Int, Boolean, etc.) or null changed.FieldAdded / FieldRemoved: A property was added or removed (e.g., when diffing polymorphic types).ElementAdded / ElementRemoved: An element was added to or removed from a List or Set.MapEntryAdded / MapEntryRemoved: A key-value pair was added to or removed from a Map.TypeMismatch: The structure changed significantly (e.g., a field changed from a primitive to a nested object).This project is licensed under the Apache License, Version 2.0.