Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions .github/DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,10 @@ lib/src/main/kotlin/dev/hossain/json5kt/
**Key Components:**

1. **JSON5** - Main API facade
- `parseToJsonElement()` - Parse JSON5 to JsonElement
- `encodeToString()` - Serialize objects to JSON5
- `decodeFromString()` - Deserialize JSON5 to objects
- `parse()` - Parse JSON5 to JSON5Value objects
- `stringify()` - Convert Kotlin objects to JSON5 strings
- `encodeToString()` - Serialize objects to JSON5 using kotlinx.serialization
- `decodeFromString()` - Deserialize JSON5 to objects using kotlinx.serialization

2. **JSON5Parser** - Core parsing engine
- Handles JSON5 syntax: comments, unquoted keys, trailing commas
Expand Down Expand Up @@ -204,9 +205,9 @@ open lib/build/reports/kover/html/index.html
1. **Parsing Errors**
```kotlin
try {
val result = JSON5.parseToJsonElement(json5Text)
val result = JSON5.parse(json5Text)
} catch (e: JSON5Exception) {
println("Parse error at position ${e.position}: ${e.message}")
println("Parse error at line ${e.lineNumber}, column ${e.columnNumber}: ${e.message}")
}
```

Expand Down
19 changes: 19 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,24 @@
- [ ] Code refactoring
- [ ] Test improvements

## Testing

<!-- Describe how you tested these changes -->

- [ ] Tests pass locally with my changes
- [ ] I have added tests that prove my fix is effective or that my feature works
- [ ] New and existing unit tests pass locally with my changes

## Checklist

<!-- Check all that apply -->

- [ ] My code follows the code style of this project
- [ ] I have run `./gradlew formatKotlin` to format my code
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [ ] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my feature works

<!-- Add any other context about the PR here -->
15 changes: 15 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,18 @@ build/
# Benchmark result files
benchmark_results_*.csv
benchmark_summary_*.txt

# Editor temporary files
*.swp
*.swo
*~
.vscode/settings.json

# OS generated files
Thumbs.db
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
18 changes: 16 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,10 +135,24 @@ class JSON5ParserTest {
""".trimIndent()

// When
val result = JSON5.parseToJsonElement(json5)
val result = JSON5.parse(json5)

// Then
// assertions...
assertThat(result).isInstanceOf(JSON5Value.Object::class.java)
val obj = result as JSON5Value.Object
assertThat((obj.value["name"] as JSON5Value.String).value).isEqualTo("test")
assertThat((obj.value["value"] as JSON5Value.Number.Integer).value).isEqualTo(42)
}

@Test
fun `should throw exception for invalid JSON5`() {
// Given
val invalidJson5 = "{ invalid: syntax }"

// When & Then
assertThrows<JSON5Exception> {
JSON5.parse(invalidJson5)
}
}
}
```
Expand Down
121 changes: 84 additions & 37 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,12 +95,15 @@ To access GitHub Packages, you need to authenticate with GitHub. You can do this

You can generate a personal access token at [GitHub > Settings > Developer settings > Personal access tokens](https://github.com/settings/tokens) with `read:packages` permission.

> **Note**: Make sure to use the **classic** personal access token format, not the new fine-grained tokens, as GitHub Packages currently requires classic tokens.

## Usage

### Basic Parsing and Stringifying

```kotlin
import dev.hossain.json5kt.JSON5
import dev.hossain.json5kt.JSON5Exception

// Parse JSON5 to strongly-typed JSON5Value objects
val json5 = """
Expand All @@ -112,20 +115,24 @@ val json5 = """
}
"""

val parsed = JSON5.parse(json5)
// Returns: JSON5Value.Object

// Access values in a type-safe way
when (parsed) {
is JSON5Value.Object -> {
val name = parsed.value["name"] as? JSON5Value.String
val version = parsed.value["version"] as? JSON5Value.Number.Integer
val features = parsed.value["features"] as? JSON5Value.Array

println("App name: ${name?.value}") // "MyApp"
println("Version: ${version?.value}") // 2
println("Features: ${features?.value?.map { (it as JSON5Value.String).value }}") // ["auth", "analytics"]
try {
val parsed = JSON5.parse(json5)
// Returns: JSON5Value.Object

// Access values in a type-safe way
when (parsed) {
is JSON5Value.Object -> {
val name = parsed.value["name"] as? JSON5Value.String
val version = parsed.value["version"] as? JSON5Value.Number.Integer
val features = parsed.value["features"] as? JSON5Value.Array

println("App name: ${name?.value}") // "MyApp"
println("Version: ${version?.value}") // 2
println("Features: ${features?.value?.map { (it as JSON5Value.String).value }}") // ["auth", "analytics"]
}
}
} catch (e: JSON5Exception) {
println("Failed to parse JSON5: ${e.message}")
}

// Stringify Kotlin objects to JSON5
Expand All @@ -142,7 +149,9 @@ val json5String = JSON5.stringify(data)

```kotlin
import dev.hossain.json5kt.JSON5
import dev.hossain.json5kt.JSON5Exception
import kotlinx.serialization.Serializable
import kotlinx.serialization.SerializationException

@Serializable
data class Config(
Expand All @@ -160,28 +169,35 @@ val config = Config(
settings = mapOf("theme" to "dark", "lang" to "en")
)

val json5 = JSON5.encodeToString(Config.serializer(), config)
// Result: {appName:'MyApp',version:2,features:['auth','analytics'],settings:{theme:'dark',lang:'en'}}

// Deserialize from JSON5 (with comments and formatting)
val json5WithComments = """
{
// Application configuration
appName: 'MyApp',
version: 2, // current version
features: [
'auth',
'analytics', // trailing comma OK
],
settings: {
theme: 'dark',
lang: 'en',
try {
val json5 = JSON5.encodeToString(Config.serializer(), config)
// Result: {appName:'MyApp',version:2,features:['auth','analytics'],settings:{theme:'dark',lang:'en'}}

// Deserialize from JSON5 (with comments and formatting)
val json5WithComments = """
{
// Application configuration
appName: 'MyApp',
version: 2, // current version
features: [
'auth',
'analytics', // trailing comma OK
],
settings: {
theme: 'dark',
lang: 'en',
}
}
"""

val decoded = JSON5.decodeFromString(Config.serializer(), json5WithComments)
// Returns: Config instance
println("Loaded config: $decoded")
} catch (e: JSON5Exception) {
println("JSON5 parsing error: ${e.message}")
} catch (e: SerializationException) {
println("Serialization error: ${e.message}")
}
"""

val decoded = JSON5.decodeFromString(Config.serializer(), json5WithComments)
// Returns: Config instance
```

### Advanced Features
Expand Down Expand Up @@ -243,12 +259,43 @@ when (complex) {
This project uses [Gradle](https://gradle.org/) with Java 21:

```bash
./gradlew build # Build the library
./gradlew test # Run tests
./gradlew check # Run all checks including tests
./gradlew :benchmark:run # Runs the benchmark
./gradlew build # Build all modules
./gradlew test # Run tests
./gradlew check # Run all checks including tests, linting, and coverage
./gradlew formatKotlin # Format code with ktlint
./gradlew :benchmark:run # Run performance benchmarks
./gradlew koverHtmlReport # Generate code coverage report
```

### Requirements

- **Java 21** or later
- **Git** (for submodules containing JSON5 specification)

## Troubleshooting

### Common Issues

**Build fails with "Build requires Java 21"**
- Ensure you have Java 21 installed: `java -version`
- Set `JAVA_HOME` environment variable to Java 21 installation

**Gradle daemon issues**
- Stop the daemon: `./gradlew --stop`
- Try again with: `./gradlew build --no-daemon`

**GitHub Packages authentication issues**
- Verify your GitHub username and personal access token
- Ensure the token has `read:packages` permission
- Use classic tokens, not fine-grained tokens

**Submodule not initialized**
```bash
git submodule update --init --recursive
```

For more detailed troubleshooting, see our [Development Guide](.github/DEVELOPMENT.md).

## Contributing

We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details on:
Expand Down
50 changes: 50 additions & 0 deletions app/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# JSON5 Demo Application

This module contains a demonstration application that showcases the json5-kotlin library's features and capabilities.

## What it demonstrates

- **Basic JSON5 parsing and stringification**
- **kotlinx.serialization integration** with custom data classes
- **Advanced JSON5 features** including:
- Comments in JSON5 files
- Trailing commas
- Unquoted keys
- Different number formats (hex, scientific notation, special values)
- Multi-line strings
- Various string quote styles
- **Real-world JSON5 file processing** from the `src/main/resources` directory
- **Error handling** and validation examples

## Running the demo

To run the demonstration application:

```bash
./gradlew :app:run
```

This will execute various examples showing:
1. Employee serialization/deserialization with kotlinx.serialization
2. All code examples from the README.md validation
3. Processing of sample JSON5 files with different features

## Sample JSON5 files

The `src/main/resources` directory contains example JSON5 files demonstrating:

- `simple-object.json5` - Basic object with comments and mixed quote styles
- `array-example.json5` - Arrays with different data types and trailing commas
- `numeric-formats.json5` - Various number formats supported by JSON5
- `string-and-identifiers.json5` - String escape sequences and special identifiers
- `root-string.json5` - Root-level string value (valid in JSON5)

## Use as a testing ground

This app module serves as:
- A **validation tool** to ensure the library works correctly
- A **reference implementation** showing best practices
- A **testing ground** for experimenting with JSON5 features
- **Living documentation** of the library's capabilities

The code in this module is designed to be readable and educational, providing practical examples of how to use the json5-kotlin library in real applications.
37 changes: 30 additions & 7 deletions benchmark/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,18 @@ To run the benchmark module tests:
./gradlew :benchmark:test
```

## Sample Results
## Performance Expectations

Based on typical runs across the three libraries:

- **JSON** (kotlinx.serialization) is consistently the fastest
- **External-JSON5** performs better than this project's JSON5 implementation
- **JSON5** (this project) offers kotlinx.serialization integration but with slower performance
- **JSON** (kotlinx.serialization) is consistently the fastest (~4-5x faster than JSON5 implementations)
- **External-JSON5** (at.syntaxerror.json5) provides good JSON5 performance (~2x faster than this project)
- **JSON5** (this project) prioritizes kotlinx.serialization integration over raw performance

This performance trade-off is expected because:
- JSON5 parsing requires additional processing for comments, flexible syntax, and extended number formats
- This project integrates with kotlinx.serialization which adds serialization overhead
- External JSON5 libraries may use optimized parsing techniques not compatible with kotlinx.serialization

Example output:
```
Expand Down Expand Up @@ -102,9 +107,27 @@ Company Serialization: JSON5=0.233ms, JSON=0.056ms, External-JSON5=0.073ms
- **JSON** is **4.45×** faster than **JSON5** and **2.51×** faster than **External-JSON5**
- **External-JSON5** is **1.77×** faster than **JSON5**

## Interpreting Results

When choosing between implementations, consider:

- **Use JSON** (kotlinx.serialization) if you need maximum performance and don't require JSON5 features
- **Use External-JSON5** if you need JSON5 features with good performance and don't need kotlinx.serialization integration
- **Use this JSON5 implementation** if you need seamless kotlinx.serialization integration with JSON5 features

The performance differences are most noticeable with:
- Large files (>1MB)
- High-frequency operations (>1000 operations/second)
- Resource-constrained environments

For typical configuration files and small-to-medium data, the performance difference may not be significant compared to the development benefits of kotlinx.serialization integration.

## Key Insights

- **kotlinx.serialization JSON** remains the performance leader
- **External JSON5 library** provides a good balance of JSON5 features with reasonable performance
- **This project's JSON5** offers seamless kotlinx.serialization integration but at a performance cost
- **kotlinx.serialization JSON** remains the performance leader for standard JSON operations
- **External JSON5 library** provides a good balance of JSON5 features with reasonable performance
- **This project's JSON5** offers seamless kotlinx.serialization integration but with a performance trade-off
- Performance differences are most significant in high-throughput scenarios
- Choose based on your priorities: performance (JSON), JSON5 features with good performance (External-JSON5), or kotlinx.serialization integration (this project)

> **Note**: These benchmarks focus on raw parsing/serialization performance. In real applications, the convenience and type safety of kotlinx.serialization integration may outweigh the performance difference for many use cases.
2 changes: 1 addition & 1 deletion settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ plugins {

// Include the `app`, `lib` and `benchmark` subprojects in the build.
// If there are changes in only one of the projects, Gradle will rebuild only the one that has changed.
// Learn more about structuring projects with Gradle - https://docs.gradle.org/8.7/userguide/multi_project_builds.html
// Learn more about structuring projects with Gradle - https://docs.gradle.org/8.14.2/userguide/multi_project_builds.html
include(":app")
include(":lib")
include(":benchmark")
Expand Down