Skip to content

Commit 1e51e91

Browse files
feat(json_handler): implement parseJsonString method with error handling
1 parent 6627f3b commit 1e51e91

File tree

2 files changed

+411
-0
lines changed

2 files changed

+411
-0
lines changed

lib/src/utils/json_handler.dart

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import 'package:flutter_policy_engine/src/exceptions/json_parse_exception.dart';
22
import 'package:flutter_policy_engine/src/exceptions/json_serialize_exception.dart';
33
import 'package:flutter_policy_engine/src/utils/log_handler.dart';
4+
import 'dart:convert';
45

56
/// Utility for type-safe JSON conversions with generic support.
67
///
@@ -262,4 +263,102 @@ class JsonHandler {
262263
return null;
263264
}
264265
}
266+
267+
/// Parses a JSON string into a Map<String, dynamic> with comprehensive error handling.
268+
///
269+
/// This function safely converts a JSON string representation into a strongly-typed
270+
/// map structure. It includes validation to ensure the parsed result is actually
271+
/// a map and provides detailed error information for debugging.
272+
///
273+
/// The function handles various error scenarios:
274+
/// - Invalid JSON syntax
275+
/// - JSON that doesn't represent an object (e.g., arrays, primitives)
276+
/// - Empty or null input strings
277+
///
278+
/// Returns a [Map<String, dynamic>] containing the parsed JSON data.
279+
///
280+
/// Throws [JsonParseException] if the JSON string cannot be parsed or doesn't
281+
/// represent a valid JSON object.
282+
///
283+
/// Example usage:
284+
/// ```dart
285+
/// try {
286+
/// final jsonString = '{"name": "John", "age": 30}';
287+
/// final result = JsonHandler.parseJsonString(jsonString);
288+
/// print(result['name']); // Output: John
289+
/// } catch (e) {
290+
/// print('Failed to parse JSON: $e');
291+
/// }
292+
/// ```
293+
static Map<String, dynamic> parseJsonString(
294+
String jsonString, {
295+
String? context,
296+
}) {
297+
LogHandler.debug(
298+
'Starting JSON string parsing',
299+
context: {
300+
'input_length': jsonString.length,
301+
'context': context ?? 'unknown',
302+
},
303+
operation: 'json_parse_string',
304+
);
305+
306+
try {
307+
// Validate input
308+
if (jsonString.isEmpty) {
309+
throw JsonParseException(
310+
'Cannot parse empty JSON string',
311+
);
312+
}
313+
314+
// Parse JSON string
315+
final dynamic parsed = jsonDecode(jsonString);
316+
317+
// Validate that the parsed result is a map
318+
if (parsed is! Map<String, dynamic>) {
319+
throw JsonParseException(
320+
'JSON string does not represent a valid object. '
321+
'Expected Map<String, dynamic>, got ${parsed.runtimeType}',
322+
originalError: TypeError(),
323+
);
324+
}
325+
326+
LogHandler.debug(
327+
'Successfully parsed JSON string',
328+
context: {
329+
'parsed_keys_count': parsed.length,
330+
'context': context ?? 'unknown',
331+
},
332+
operation: 'json_parse_string_success',
333+
);
334+
335+
return parsed;
336+
} catch (e, stackTrace) {
337+
final errorMessage = 'Failed to parse JSON string: ${e.toString()}';
338+
339+
LogHandler.error(
340+
errorMessage,
341+
error: e,
342+
stackTrace: stackTrace,
343+
context: {
344+
'input_length': jsonString.length,
345+
'input_preview': jsonString.length > 100
346+
? '${jsonString.substring(0, 100)}...'
347+
: jsonString,
348+
'context': context ?? 'unknown',
349+
},
350+
operation: 'json_parse_string_error',
351+
);
352+
353+
// Re-throw as JsonParseException if it's not already
354+
if (e is JsonParseException) {
355+
rethrow;
356+
}
357+
358+
throw JsonParseException(
359+
errorMessage,
360+
originalError: e,
361+
);
362+
}
363+
}
265364
}

0 commit comments

Comments
 (0)