Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 30 additions & 64 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ JsonValue value = Json.parse(json);

// Access as map-like structure
JsonObject obj = (JsonObject) value;
String name = ((JsonString) obj.members().get("name")).value();
int age = ((JsonNumber) obj.members().get("age")).intValue();
boolean active = ((JsonBoolean) obj.members().get("active")).value();
String name = ((JsonString) obj.members().get("name")).string();
int age = Math.toIntExact(((JsonNumber) obj.members().get("age")).toLong());
boolean active = ((JsonBoolean) obj.members().get("active")).bool();
```

### Simple Record Mapping
Expand All @@ -58,56 +58,22 @@ JsonObject jsonObj = (JsonObject) Json.parse(userJson);

// Map to record
User user = new User(
((JsonString) jsonObj.members().get("name")).value(),
((JsonNumber) jsonObj.members().get("age")).intValue(),
((JsonBoolean) jsonObj.members().get("active")).value()
((JsonString) jsonObj.members().get("name")).string(),
Math.toIntExact(((JsonNumber) jsonObj.members().get("age")).toLong()),
((JsonBoolean) jsonObj.members().get("active")).bool()
);

// Convert records back to JSON
JsonValue backToJson = Json.fromUntyped(Map.of(
"name", user.name(),
"age", user.age(),
"active", user.active()
JsonValue backToJson = JsonObject.of(Map.of(
"name", JsonString.of(user.name()),
"age", JsonNumber.of(user.age()),
"active", JsonBoolean.of(user.active())
));

// Covert back to a JSON string
String jsonString = backToJson.toString();
```

### Converting from Java Objects to JSON (`fromUntyped`)

```java
// Convert standard Java collections to JsonValue
Map<String, Object> data = Map.of(
"name", "John",
"age", 30,
"scores", List.of(85, 92, 78)
);
JsonValue json = Json.fromUntyped(data);
```

### Converting from JSON to Java Objects (`toUntyped`)

```java
// Convert JsonValue back to standard Java types
JsonValue parsed = Json.parse("{\"name\":\"John\",\"age\":30}");
Object data = Json.toUntyped(parsed);
// Returns a Map<String, Object> with standard Java types
```

The conversion mappings are:
- `JsonObject` ↔ `Map<String, Object>`
- `JsonArray` ↔ `List<Object>`
- `JsonString` ↔ `String`
- `JsonNumber` ↔ `Number` (Long, Double, BigInteger, or BigDecimal)
- `JsonBoolean` ↔ `Boolean`
- `JsonNull` ↔ `null`

This is useful for:
- Integrating with existing code that uses standard collections
- Serializing/deserializing to formats that expect Java types
- Working with frameworks that use reflection on standard types

### Realistic Record Mapping

A powerful feature is mapping between Java records and JSON:
Expand All @@ -124,28 +90,28 @@ Team team = new Team("Engineering", List.of(
));

// Convert records to JSON
JsonValue teamJson = Json.fromUntyped(Map.of(
"teamName", team.teamName(),
"members", team.members().stream()
.map(u -> Map.of(
"name", u.name(),
"email", u.email(),
"active", u.active()
))
.toList()
JsonValue teamJson = JsonObject.of(Map.of(
"teamName", JsonString.of(team.teamName()),
"members", JsonArray.of(team.members().stream()
.map(u -> JsonObject.of(Map.of(
"name", JsonString.of(u.name()),
"email", JsonString.of(u.email()),
"active", JsonBoolean.of(u.active())
)))
.toList())
));

// Parse JSON back to records
JsonObject parsed = (JsonObject) Json.parse(teamJson.toString());
Team reconstructed = new Team(
((JsonString) parsed.members().get("teamName")).value(),
((JsonArray) parsed.members().get("members")).values().stream()
((JsonString) parsed.members().get("teamName")).string(),
((JsonArray) parsed.members().get("members")).elements().stream()
.map(v -> {
JsonObject member = (JsonObject) v;
return new User(
((JsonString) member.members().get("name")).value(),
((JsonString) member.members().get("email")).value(),
((JsonBoolean) member.members().get("active")).value()
((JsonString) member.members().get("name")).string(),
((JsonString) member.members().get("email")).string(),
((JsonBoolean) member.members().get("active")).bool()
);
})
.toList()
Expand Down Expand Up @@ -182,10 +148,10 @@ Process JSON arrays efficiently with Java streams:
```java
// Filter active users from a JSON array
JsonArray users = (JsonArray) Json.parse(jsonArrayString);
List<String> activeUserEmails = users.values().stream()
List<String> activeUserEmails = users.elements().stream()
.map(v -> (JsonObject) v)
.filter(obj -> ((JsonBoolean) obj.members().get("active")).value())
.map(obj -> ((JsonString) obj.members().get("email")).value())
.filter(obj -> ((JsonBoolean) obj.members().get("active")).bool())
.map(obj -> ((JsonString) obj.members().get("email")).string())
.toList();
```

Expand All @@ -198,9 +164,9 @@ try {
JsonValue value = Json.parse(userInput);
// Process valid JSON
} catch (JsonParseException e) {
// Handle malformed JSON with line/column information
System.err.println("Invalid JSON at line " + e.getLine() +
", column " + e.getColumn() + ": " + e.getMessage());
// Handle malformed JSON with line/position information
System.err.println("Invalid JSON at line " + e.getErrorLine() +
", position " + e.getErrorPosition() + ": " + e.getMessage());
}
```

Expand Down
22 changes: 11 additions & 11 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,8 @@ <h3>Simple Example</h3>
JsonObject obj = (JsonObject) value;

// Access values
String name = ((JsonString) obj.members().get("name")).value();
int age = ((JsonNumber) obj.members().get("age")).toNumber().intValue();</code></pre>
String name = ((JsonString) obj.members().get("name")).string();
int age = Math.toIntExact(((JsonNumber) obj.members().get("age")).toLong());</code></pre>

<h2>Key Features</h2>
<ul>
Expand All @@ -118,15 +118,15 @@ <h2>Record Mapping Example</h2>
new User("Bob", "bob@example.com", false)
));

JsonValue teamJson = Json.fromUntyped(Map.of(
"teamName", team.teamName(),
"members", team.members().stream()
.map(u -> Map.of(
"name", u.name(),
"email", u.email(),
"active", u.active()
))
.toList()
JsonValue teamJson = JsonObject.of(Map.of(
"teamName", JsonString.of(team.teamName()),
"members", JsonArray.of(team.members().stream()
.map(u -> JsonObject.of(Map.of(
"name", JsonString.of(u.name()),
"email", JsonString.of(u.email()),
"active", JsonBoolean.of(u.active())
)))
.toList())
));</code></pre>

<h2>Resources</h2>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -599,7 +599,7 @@ static JsonObject compareApis(JsonObject local, JsonObject upstream) {
// Extract class name safely
final var localClassName = local.members().get("className");
final var className = localClassName instanceof JsonString js ?
js.value() : "Unknown";
js.string() : "Unknown";

diffMap.put("className", JsonString.of(className));

Expand All @@ -612,7 +612,7 @@ static JsonObject compareApis(JsonObject local, JsonObject upstream) {

// Check if status is NOT_IMPLEMENTED (from parsing)
if (upstream.members().containsKey("status")) {
final var status = ((JsonString) upstream.members().get("status")).value();
final var status = ((JsonString) upstream.members().get("status")).string();
if ("NOT_IMPLEMENTED".equals(status)) {
diffMap.put("status", JsonString.of("PARSE_NOT_IMPLEMENTED"));
return JsonObject.of(diffMap);
Expand Down Expand Up @@ -681,11 +681,11 @@ static boolean compareModifiers(JsonObject local, JsonObject upstream, List<Json
return false;
}

final var localSet = localMods.values().stream()
.map(v -> ((JsonString) v).value())
final var localSet = localMods.elements().stream()
.map(v -> ((JsonString) v).string())
.collect(Collectors.toSet());
final var upstreamSet = upstreamMods.values().stream()
.map(v -> ((JsonString) v).value())
final var upstreamSet = upstreamMods.elements().stream()
.map(v -> ((JsonString) v).string())
.collect(Collectors.toSet());

if (!localSet.equals(upstreamSet)) {
Expand All @@ -708,11 +708,11 @@ static boolean compareInheritance(JsonObject local, JsonObject upstream, List<Js
return false;
}

final var localTypes = localExtends.values().stream()
.map(v -> normalizeTypeName(((JsonString) v).value()))
final var localTypes = localExtends.elements().stream()
.map(v -> normalizeTypeName(((JsonString) v).string()))
.collect(Collectors.toSet());
final var upstreamTypes = upstreamExtends.values().stream()
.map(v -> normalizeTypeName(((JsonString) v).value()))
final var upstreamTypes = upstreamExtends.elements().stream()
.map(v -> normalizeTypeName(((JsonString) v).string()))
.collect(Collectors.toSet());

if (!localTypes.equals(upstreamTypes)) {
Expand Down Expand Up @@ -786,8 +786,8 @@ static boolean compareMethods(JsonObject local, JsonObject upstream, List<JsonVa
/// Compares method signatures
static boolean compareMethodSignature(JsonObject localMethod, JsonObject upstreamMethod) {
// Compare return types
final var localReturn = normalizeTypeName(((JsonString) localMethod.members().get("returnType")).value());
final var upstreamReturn = normalizeTypeName(((JsonString) upstreamMethod.members().get("returnType")).value());
final var localReturn = normalizeTypeName(((JsonString) localMethod.members().get("returnType")).string());
final var upstreamReturn = normalizeTypeName(((JsonString) upstreamMethod.members().get("returnType")).string());
if (!localReturn.equals(upstreamReturn)) {
return false;
}
Expand All @@ -796,14 +796,14 @@ static boolean compareMethodSignature(JsonObject localMethod, JsonObject upstrea
final var localParams = (JsonArray) localMethod.members().get("parameters");
final var upstreamParams = (JsonArray) upstreamMethod.members().get("parameters");

if (localParams.values().size() != upstreamParams.values().size()) {
if (localParams.elements().size() != upstreamParams.elements().size()) {
return false;
}

// Compare each parameter
for (int i = 0; i < localParams.values().size(); i++) {
final var localParam = normalizeTypeName(((JsonString) localParams.values().get(i)).value());
final var upstreamParam = normalizeTypeName(((JsonString) upstreamParams.values().get(i)).value());
for (int i = 0; i < localParams.elements().size(); i++) {
final var localParam = normalizeTypeName(((JsonString) localParams.elements().get(i)).string());
final var upstreamParam = normalizeTypeName(((JsonString) upstreamParams.elements().get(i)).string());
if (!localParam.equals(upstreamParam)) {
return false;
}
Expand Down Expand Up @@ -848,11 +848,11 @@ static boolean compareConstructors(JsonObject local, JsonObject upstream, List<J
return false;
}

if (localConstructors.values().size() != upstreamConstructors.values().size()) {
if (localConstructors.elements().size() != upstreamConstructors.elements().size()) {
differences.add(JsonObject.of(Map.of(
"type", JsonString.of("constructorsChanged"),
"localCount", JsonNumber.of(localConstructors.values().size()),
"upstreamCount", JsonNumber.of(upstreamConstructors.values().size())
"localCount", JsonNumber.of(localConstructors.elements().size()),
"upstreamCount", JsonNumber.of(upstreamConstructors.elements().size())
)));
return true;
}
Expand Down Expand Up @@ -908,7 +908,7 @@ static JsonObject runFullComparison() {
differences.add(diff);

// Count statistics
final var status = ((JsonString) diff.members().get("status")).value();
final var status = ((JsonString) diff.members().get("status")).string();
switch (status) {
case "MATCHING" -> matchingCount++;
case "UPSTREAM_ERROR" -> missingUpstream++;
Expand Down Expand Up @@ -964,21 +964,21 @@ static String generateFingerprint(JsonObject report) {
final var differences = (JsonArray) report.members().get("differences");
final var stableLines = new ArrayList<String>();

for (final var diff : differences.values()) {
for (final var diff : differences.elements()) {
final var diffObj = (JsonObject) diff;
final var status = ((JsonString) diffObj.members().get("status")).value();
final var status = ((JsonString) diffObj.members().get("status")).string();

if (!"DIFFERENT".equals(status)) continue;

final var className = ((JsonString) diffObj.members().get("className")).value();
final var className = ((JsonString) diffObj.members().get("className")).string();
final var classDiffs = (JsonArray) diffObj.members().get("differences");

if (classDiffs != null) {
for (final var change : classDiffs.values()) {
for (final var change : classDiffs.elements()) {
final var changeObj = (JsonObject) change;
final var type = ((JsonString) changeObj.members().get("type")).value();
final var type = ((JsonString) changeObj.members().get("type")).string();
final var methodValue = changeObj.members().get("method");
final var method = methodValue instanceof JsonString js ? js.value() : "";
final var method = methodValue instanceof JsonString js ? js.string() : "";
// Create stable line: "ClassName:changeType:methodName"
stableLines.add(className + ":" + type + ":" + method);
}
Expand Down Expand Up @@ -1013,7 +1013,7 @@ private static long getDifferentApiCount(JsonObject report) {
}
final var differentApiValue = summary.members().get("differentApi");
if (differentApiValue instanceof JsonNumber num) {
return num.toNumber().longValue();
return num.toLong();
}
return 0;
}
Expand All @@ -1027,10 +1027,10 @@ static String generateSummary(JsonObject report) {
final var summary = (JsonObject) report.members().get("summary");
final var differences = (JsonArray) report.members().get("differences");

final var totalClasses = ((JsonNumber) summary.members().get("totalClasses")).toNumber().longValue();
final var matchingClasses = ((JsonNumber) summary.members().get("matchingClasses")).toNumber().longValue();
final var totalClasses = ((JsonNumber) summary.members().get("totalClasses")).toLong();
final var matchingClasses = ((JsonNumber) summary.members().get("matchingClasses")).toLong();
final var differentApi = getDifferentApiCount(report);
final var missingUpstream = ((JsonNumber) summary.members().get("missingUpstream")).toNumber().longValue();
final var missingUpstream = ((JsonNumber) summary.members().get("missingUpstream")).toLong();

sb.append("## API Comparison Summary\n\n");
sb.append("| Metric | Count |\n");
Expand All @@ -1043,22 +1043,22 @@ static String generateSummary(JsonObject report) {
if (differentApi > 0) {
sb.append("## Changes Detected\n\n");

for (final var diff : differences.values()) {
for (final var diff : differences.elements()) {
final var diffObj = (JsonObject) diff;
final var status = ((JsonString) diffObj.members().get("status")).value();
final var status = ((JsonString) diffObj.members().get("status")).string();

if (!"DIFFERENT".equals(status)) continue;

final var className = ((JsonString) diffObj.members().get("className")).value();
final var className = ((JsonString) diffObj.members().get("className")).string();
sb.append("### ").append(className).append("\n\n");

final var classDiffs = (JsonArray) diffObj.members().get("differences");
if (classDiffs != null) {
for (final var change : classDiffs.values()) {
for (final var change : classDiffs.elements()) {
final var changeObj = (JsonObject) change;
final var type = ((JsonString) changeObj.members().get("type")).value();
final var type = ((JsonString) changeObj.members().get("type")).string();
final var methodValue = changeObj.members().get("method");
final var method = methodValue instanceof JsonString js ? js.value() : "unknown";
final var method = methodValue instanceof JsonString js ? js.string() : "unknown";

final var emoji = switch (type) {
case "methodRemoved" -> "➖";
Expand All @@ -1078,7 +1078,7 @@ static String generateSummary(JsonObject report) {
}

sb.append("---\n");
final var timestamp = ((JsonString) report.members().get("timestamp")).value();
final var timestamp = ((JsonString) report.members().get("timestamp")).string();
sb.append("*Generated by API Tracker on ").append(timestamp.split("T")[0]).append("*\n");

return sb.toString();
Expand All @@ -1090,4 +1090,4 @@ static String generateSummary(JsonObject report) {
static boolean hasDifferences(JsonObject report) {
return getDifferentApiCount(report) > 0;
}
}
}
Loading
Loading