Skip to content

Commit 9ff9b98

Browse files
Add chainable JSON access with JsonMap to documentation, including examples in README, API reference, and usage guides for Java and Kotlin. Update contact email for corporate sponsorship.
1 parent d3efae5 commit 9ff9b98

7 files changed

Lines changed: 411 additions & 1 deletion

File tree

README.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,17 @@ fun main() {
122122
}
123123
```
124124

125+
**Chainable JSON Access:**
126+
```java
127+
import io.mochaapi.client.*;
128+
129+
// Clean nested JSON access without casting
130+
String city = Api.get("https://api.example.com/user/123")
131+
.execute()
132+
.toJsonMap()
133+
.get("data").get("location").get("city").toString();
134+
```
135+
125136
## Core Capabilities
126137

127138
**Production-Ready Features:**
@@ -208,7 +219,7 @@ MochaJSON is open source and free to use. If it helps your project, consider spo
208219
### 🏢 Corporate Sponsorship
209220

210221
For enterprise support, custom features, or bulk licensing, contact us at:
211-
- 📧 Email: sponsors@mochaapi.org
222+
- 📧 Email: kashvi0712@proton.me
212223
- 💼 GitHub: [Enterprise Contact](https://github.com/MochaAPI)
213224

214225
## Comparison with Alternatives

docs/docs/api/api-reference.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,7 @@ Container for HTTP response data with JSON parsing capabilities.
251251
| `json()` | Get JSON mapper instance | None | `JsonMapper` |
252252
| `to(Class<T> type)` | Parse JSON to specified type | `type` - Target class | `T` |
253253
| `toMap()` | Parse JSON to Map | None | `Map<String, Object>` |
254+
| `toJsonMap()` | Parse JSON to chainable JsonMap | None | `JsonMap` |
254255
| `toList()` | Parse JSON to List | None | `List&lt;Object&gt;` |
255256
| `isSuccess()` | Check if status is 200-299 | None | `boolean` |
256257
| `isError()` | Check if status is 400+ | None | `boolean` |
@@ -268,6 +269,7 @@ Map<String, String> headers = response.headers();
268269
// JSON parsing
269270
User user = response.to(User.class);
270271
Map<String, Object> data = response.toMap();
272+
JsonMap json = response.toJsonMap();
271273
List&lt;Object&gt; items = response.toList();
272274

273275
// Status checking
@@ -278,6 +280,37 @@ if (response.isSuccess()) {
278280
}
279281
```
280282

283+
### `JsonMap` Class
284+
285+
Chainable wrapper for JSON data that eliminates casting boilerplate when accessing nested objects.
286+
287+
#### Constructors
288+
289+
| Constructor | Description | Parameters |
290+
|-------------|-------------|------------|
291+
| `JsonMap(Map<String, Object> map)` | Create JsonMap from Map | `map` - Map to wrap |
292+
293+
#### Methods
294+
295+
| Method | Description | Parameters | Returns |
296+
|--------|-------------|------------|---------|
297+
| `get(String key)` | Get value with chainable access | `key` - Key to retrieve | `JsonMap` |
298+
| `toString()` | Get string representation | None | `String` |
299+
300+
#### Example Usage
301+
302+
```java
303+
JsonMap json = response.toJsonMap();
304+
305+
// Chainable nested access
306+
String city = json.get("data").get("location").get("city").toString();
307+
String latitude = json.get("data").get("location").get("coordinates").get("latitude").toString();
308+
309+
// Intermediate access
310+
JsonMap user = json.get("data").get("user");
311+
String name = user.get("name").get("first").toString();
312+
```
313+
281314
## JSON Mapping
282315

283316
### `JsonMapper` Interface

docs/docs/usage/java-examples.md

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,70 @@ public class SecurityExample {
139139
}
140140
```
141141

142+
### Nested JSON Access with JsonMap
143+
144+
When working with deeply nested API responses, JsonMap eliminates the need for verbose casting and provides a clean, chainable API.
145+
146+
```java
147+
import io.mochaapi.client.*;
148+
149+
public class JsonMapExample {
150+
public static void main(String[] args) {
151+
try {
152+
// Example: User profile API with nested data
153+
JsonMap response = Api.get("https://api.example.com/user/123")
154+
.execute()
155+
.toJsonMap();
156+
157+
// Traditional approach (verbose with casting)
158+
Map<String, Object> data = response.toMap();
159+
Map<String, Object> user = (Map<String, Object>) data.get("data");
160+
Map<String, Object> name = (Map<String, Object>) user.get("name");
161+
Map<String, Object> location = (Map<String, Object>) user.get("location");
162+
Map<String, Object> street = (Map<String, Object>) location.get("street");
163+
Map<String, Object> coordinates = (Map<String, Object>) location.get("coordinates");
164+
165+
String traditionalName = name.get("first") + " " + name.get("last");
166+
String traditionalAddress = street.get("number") + " " + street.get("name") + ", " + location.get("city");
167+
String traditionalLat = coordinates.get("latitude").toString();
168+
169+
// JsonMap approach (clean chaining)
170+
String cleanName = response.get("data").get("name").get("first") + " " +
171+
response.get("data").get("name").get("last");
172+
String cleanAddress = response.get("data").get("location").get("street").get("number") + " " +
173+
response.get("data").get("location").get("street").get("name") + ", " +
174+
response.get("data").get("location").get("city");
175+
String cleanLat = response.get("data").get("location").get("coordinates").get("latitude").toString();
176+
177+
System.out.println("Name: " + cleanName);
178+
System.out.println("Address: " + cleanAddress);
179+
System.out.println("Latitude: " + cleanLat);
180+
181+
// Intermediate access for complex operations
182+
JsonMap userData = response.get("data");
183+
JsonMap locationData = userData.get("location");
184+
185+
String email = userData.get("email").toString();
186+
String city = locationData.get("city").toString();
187+
String state = locationData.get("state").toString();
188+
189+
System.out.println("Email: " + email);
190+
System.out.println("Location: " + city + ", " + state);
191+
192+
} catch (ApiException | JsonException e) {
193+
e.printStackTrace();
194+
}
195+
}
196+
}
197+
```
198+
199+
**Benefits of JsonMap:**
200+
201+
- **No casting boilerplate**: Eliminates `(Map<String, Object>)` casts
202+
- **Type safety**: Prevents ClassCastException errors
203+
- **Readable code**: Chainable syntax reads naturally
204+
- **Flexible access**: Supports both direct chaining and intermediate variables
205+
142206
### Multiple Clients Pattern
143207

144208
```java

docs/docs/usage/json-handling.md

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,112 @@ Post post = Api.get("https://jsonplaceholder.typicode.com/posts/1")
4545
System.out.println("Title: " + post.title);
4646
```
4747

48+
## Chainable JSON Access with JsonMap
49+
50+
When working with deeply nested JSON responses, the traditional approach requires multiple explicit casts to `Map<String, Object>`, which creates verbose and error-prone code. JsonMap provides a clean, chainable alternative.
51+
52+
### The Problem: Casting Boilerplate
53+
54+
```java
55+
// Traditional approach - verbose and error-prone
56+
Map<String, Object> response = Api.get("https://api.example.com/user").execute().toMap();
57+
58+
// Accessing nested data requires multiple casts
59+
String city = ((Map<String, Object>)((Map<String, Object>)response.get("data"))
60+
.get("location")).get("city").toString();
61+
62+
String latitude = ((Map<String, Object>)((Map<String, Object>)((Map<String, Object>)response.get("data"))
63+
.get("location")).get("coordinates")).get("latitude").toString();
64+
```
65+
66+
### The Solution: JsonMap Chaining
67+
68+
```java
69+
// JsonMap approach - clean and readable
70+
JsonMap json = Api.get("https://api.example.com/user").execute().toJsonMap();
71+
72+
// Clean chaining without casting
73+
String city = json.get("data").get("location").get("city").toString();
74+
String latitude = json.get("data").get("location").get("coordinates").get("latitude").toString();
75+
```
76+
77+
### Real-World Example
78+
79+
Consider this nested JSON response from a user API:
80+
81+
```json
82+
{
83+
"statusCode": 200,
84+
"data": {
85+
"name": {
86+
"title": "Ms",
87+
"first": "Kitty",
88+
"last": "Wallace"
89+
},
90+
"location": {
91+
"street": {
92+
"number": 1103,
93+
"name": "Valley View Ln"
94+
},
95+
"city": "Bridgeport",
96+
"state": "Minnesota",
97+
"country": "United States",
98+
"coordinates": {
99+
"latitude": "-64.0863",
100+
"longitude": "-155.0095"
101+
}
102+
},
103+
"email": "kitty.wallace@example.com"
104+
},
105+
"success": true
106+
}
107+
```
108+
109+
**Traditional approach:**
110+
```java
111+
Map<String, Object> response = Api.get("https://api.example.com/user").execute().toMap();
112+
Map<String, Object> data = (Map<String, Object>) response.get("data");
113+
Map<String, Object> name = (Map<String, Object>) data.get("name");
114+
Map<String, Object> location = (Map<String, Object>) data.get("location");
115+
Map<String, Object> street = (Map<String, Object>) location.get("street");
116+
Map<String, Object> coordinates = (Map<String, Object>) location.get("coordinates");
117+
118+
String fullName = name.get("first") + " " + name.get("last");
119+
String address = street.get("number") + " " + street.get("name") + ", " + location.get("city");
120+
String lat = coordinates.get("latitude").toString();
121+
```
122+
123+
**JsonMap approach:**
124+
```java
125+
JsonMap json = Api.get("https://api.example.com/user").execute().toJsonMap();
126+
127+
String fullName = json.get("data").get("name").get("first") + " " + json.get("data").get("name").get("last");
128+
String address = json.get("data").get("location").get("street").get("number") + " " +
129+
json.get("data").get("location").get("street").get("name") + ", " +
130+
json.get("data").get("location").get("city");
131+
String lat = json.get("data").get("location").get("coordinates").get("latitude").toString();
132+
```
133+
134+
### Benefits of JsonMap
135+
136+
1. **Eliminates casting boilerplate** - No more explicit `(Map<String, Object>)` casts
137+
2. **Prevents unchecked cast warnings** - Type-safe access to nested data
138+
3. **Improves readability** - Clean, chainable syntax that reads naturally
139+
4. **Reduces errors** - No risk of ClassCastException from incorrect casting
140+
5. **Seamless integration** - Works alongside existing `toMap()` and `to()` methods
141+
142+
### Kotlin Usage
143+
144+
```kotlin
145+
import io.mochaapi.client.*
146+
147+
val json = Api.get("https://api.example.com/user").execute().toJsonMap()
148+
149+
val fullName = "${json.get("data").get("name").get("first")} ${json.get("data").get("name").get("last")}"
150+
val city = json.get("data").get("location").get("city").toString()
151+
val latitude = json.get("data").get("location").get("coordinates").get("latitude").toString()
152+
```
153+
48154
## Nested Object Parsing
49155

50156
### Java Example

docs/docs/usage/kotlin-examples.md

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,76 @@ fun main() {
560560
}
561561
```
562562

563+
### Nested JSON Access with JsonMap
564+
565+
JsonMap provides a clean, chainable API for accessing nested JSON data without casting boilerplate.
566+
567+
```kotlin
568+
import io.mochaapi.client.*
569+
570+
fun main() {
571+
try {
572+
// Example: User profile API with nested data
573+
val json = Api.get("https://api.example.com/user/123")
574+
.execute()
575+
.toJsonMap()
576+
577+
// Traditional approach (verbose with casting)
578+
val data = json.toMap()
579+
val user = data["data"] as Map<String, Any>
580+
val name = user["name"] as Map<String, Any>
581+
val location = user["location"] as Map<String, Any>
582+
val street = location["street"] as Map<String, Any>
583+
val coordinates = location["coordinates"] as Map<String, Any>
584+
585+
val traditionalName = "${name["first"]} ${name["last"]}"
586+
val traditionalAddress = "${street["number"]} ${street["name"]}, ${location["city"]}"
587+
val traditionalLat = coordinates["latitude"].toString()
588+
589+
// JsonMap approach (clean chaining)
590+
val cleanName = "${json.get("data").get("name").get("first")} ${json.get("data").get("name").get("last")}"
591+
val cleanAddress = "${json.get("data").get("location").get("street").get("number")} " +
592+
"${json.get("data").get("location").get("street").get("name")}, " +
593+
"${json.get("data").get("location").get("city")}"
594+
val cleanLat = json.get("data").get("location").get("coordinates").get("latitude").toString()
595+
596+
println("Name: $cleanName")
597+
println("Address: $cleanAddress")
598+
println("Latitude: $cleanLat")
599+
600+
// Intermediate access for complex operations
601+
val userData = json.get("data")
602+
val locationData = userData.get("location")
603+
604+
val email = userData.get("email").toString()
605+
val city = locationData.get("city").toString()
606+
val state = locationData.get("state").toString()
607+
608+
println("Email: $email")
609+
println("Location: $city, $state")
610+
611+
// Kotlin extension functions for even cleaner syntax
612+
fun JsonMap.safeGet(key: String): String = get(key).toString()
613+
614+
val firstName = json.get("data").get("name").safeGet("first")
615+
val lastName = json.get("data").get("name").safeGet("last")
616+
val fullName = "$firstName $lastName"
617+
618+
println("Full name: $fullName")
619+
620+
} catch (e: Exception) {
621+
println("Error: ${e.message}")
622+
}
623+
}
624+
```
625+
626+
**Benefits for Kotlin developers:**
627+
628+
- **No casting**: Eliminates `as Map<String, Any>` casts
629+
- **Null safety**: Works well with Kotlin's null safety features
630+
- **Extension functions**: Can create custom extension functions for cleaner syntax
631+
- **Coroutines friendly**: Works seamlessly with async operations
632+
563633
## Functional Style with Extension Functions
564634

565635
```kotlin

src/main/java/io/mochaapi/client/ApiResponse.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,30 @@ public <T> T to(Class<T> type) {
8989
public Map<String, Object> toMap() {
9090
return jsonMapper.toMap(body);
9191
}
92+
93+
// Returns a new JsonMap representation of the response body for convenient dot-style access.
94+
// Example: response.toJsonMap().get("user").get("name").get("first");
95+
// See: src/main/java/io/mochaapi/client/JsonMap
96+
/**
97+
* Converts the response body to a {@link JsonMap} for convenient dot-style property access.
98+
* <p>
99+
* Example usage:
100+
* <pre>
101+
* JsonMap user = response.toJsonMap().get("user");
102+
* String firstName = user.get("name").get("first").toString();
103+
* </pre>
104+
* <p>
105+
* The {@code JsonMap} wrapper allows chainable {@code get()} calls that automatically
106+
* wrap nested {@code Map}s without unchecked casts.
107+
* <p>
108+
* See {@link io.mochaapi.client.JsonMap} for usage semantics.
109+
*
110+
* @return a JsonMap representing the response body
111+
* @throws JsonException if parsing fails or the body is not valid JSON
112+
*/
113+
public JsonMap toJsonMap() {
114+
return new JsonMap(jsonMapper.toMap(body));
115+
}
92116

93117
/**
94118
* Converts the response body to a List.

0 commit comments

Comments
 (0)