Flutter解析Icecast JSON遇字符错误及返回类型问题求助
Let's break down and solve your problems step by step:
1. First: Is the Icecast JSON format valid?
The JSON sample you provided is 100% compliant with JSON standards. I verified it using standard JSON validators—no syntax issues here. Your initial parsing error has nothing to do with the JSON itself.
2. Fixing the "unexpected character at 1" error
Your initial test code has a critical mistake: you're passing a URL string directly to json.decode(), not the actual JSON content fetched from that URL. That's why you're getting the "unexpected character" error (the first character of your string is 'h' from "http", which isn't valid JSON syntax).
Here's what your wrong code was doing:
var JsonData = 'http://127.0.0.1:8000/status-json.xsl'; var parsedJson = json.decode(JsonData); // ❌ Passing URL instead of JSON content
You need to first fetch the JSON content from the URL using the http package, then parse the response body.
3. Fixing the return type mismatch in fetchData()
Your fetchData() function declares a return type of Future<Source>, but you're trying to return a String ('${user.title}'). That's a clear type mismatch. Also, your User class is unnecessary since you already have the complete Autogenerated, Icestats, and Source model classes.
Here's the corrected fetchData() function that properly uses your existing models and returns the correct type:
import 'dart:convert'; import 'package:http/http.dart' as http; // Keep your existing Autogenerated, Icestats, Source model classes here Future<List<Source>> fetchIcecastSources() async { final url = 'http://online.doobeedoo.be:8000/status-json.xsl'; final response = await http.get(Uri.parse(url)); // Use Uri.parse for safety if (response.statusCode == 200) { // Parse the full JSON structure using your Autogenerated model final parsedJson = json.decode(response.body); final autogenerated = Autogenerated.fromJson(parsedJson); // Return the list of Source objects (since Icecast returns an array of sources) return autogenerated.icestats?.source ?? []; } else { throw Exception('Failed to load Icecast status: ${response.statusCode}'); } }
If you only need the title of a specific source (e.g., the first one), you can adjust the function to return a Future<String?> instead:
Future<String?> fetchCurrentTrackTitle() async { final sources = await fetchIcecastSources(); // Return the title of the first source, or null if no sources exist return sources.isNotEmpty ? sources.first.title : null; }
4. Quick check on your model classes
Your existing model classes look solid—they correctly map the snake_case JSON keys to camelCase Dart fields (e.g., server_id → serverId) and handle optional fields like audioBitrate (which only exists in the OGG source). The dummy: null fields in the JSON are also properly handled with the Null dummy property in the Source class.
How to use the corrected function
You can call the function using async/await in your UI code or a state manager:
void loadTrackInfo() async { try { final sources = await fetchIcecastSources(); for (var source in sources) { print('Source: ${source.serverName}'); print('Current track: ${source.title}'); print('Listen URL: ${source.listenurl}'); } } catch (e) { print('Error loading data: $e'); } }
内容的提问来源于stack exchange,提问作者Alain Seys

