#json #flutter #dart
Вопрос:
Я пытаюсь динамически создать файл json на устройстве, файл успешно создан, но когда я пытаюсь выполнить код jsonDecode, он выдает эту ошибку.
Ошибка заключается в следующем:
════════ Exception caught by widgets library ═══════════════════════════════════
The following LateError was thrown building Home(dirty, state: HomeState#853db):
LateInitializationError: Field 'fileContent' has not been initialized.
The relevant error-causing widget was
Home
package:json_storage/main.dart:101
When the exception was thrown, this was the stack
#0 HomeState.fileContent (package:json_storage/main.dart)
package:json_storage/main.dart:1
#1 HomeState.build
package:json_storage/main.dart:183
#2 StatefulElement.build
package:flutter/…/widgets/framework.dart:4691
#3 ComponentElement.performRebuild
package:flutter/…/widgets/framework.dart:4574
#4 StatefulElement.performRebuild
package:flutter/…/widgets/framework.dart:4746
...
════════════════════════════════════════════════════════════════════════════════
I/flutter (23568): true
E/flutter (23568): [ERROR:flutter/lib/ui/ui_dart_state.cc(199)] Unhandled Exception: FormatException: Unexpected end of input (at character 1)
E/flutter (23568):
E/flutter (23568): ^
E/flutter (23568):
E/flutter (23568): #0 _ChunkedJsonParser.fail (dart:convert-patch/convert_patch.dart:1404:5)
E/flutter (23568): #1 _ChunkedJsonParser.close (dart:convert-patch/convert_patch.dart:522:7)
E/flutter (23568): #2 _parseJson (dart:convert-patch/convert_patch.dart:41:10)
E/flutter (23568): #3 JsonDecoder.convert (dart:convert/json.dart:506:36)
E/flutter (23568): #4 JsonCodec.decode (dart:convert/json.dart:157:41)
E/flutter (23568): #5 jsonDecode (dart:convert/json.dart:96:10)
E/flutter (23568): #6 HomeState.initState.<anonymous closure>
package:json_storage/main.dart:131
E/flutter (23568): #7 _rootRunUnary (dart:async/zone.dart:1362:47)
E/flutter (23568): #8 _CustomZone.runUnary (dart:async/zone.dart:1265:19)
E/flutter (23568): <asynchronous suspension>
E/flutter (23568):
Я уже пробовал сделать это асинхронным на всякий случай, но, похоже, это не проблема, поэтому я не знаю, что может произойти.
Здесь я оставляю код, это просто файл main.dart, у меня больше ничего нет, так как это тестовое приложение, чтобы узнать, смогу ли я создать этот json и написать в нем, также я использовал пакет path_provider, но это все, я могу поместить репозиторий на github, если необходимо, но просто скопируйте и вставьте этот код и добавьте path_provider в pubspec.yaml.
import 'package:flutter/material.dart';
import 'dart:io';
import 'dart:convert';
import 'package:path_provider/path_provider.dart';
void main() {
runApp(new MaterialApp(
home: new Home(),
));
}
class Home extends StatefulWidget {
@override
State createState() => new HomeState();
}
class HomeState extends State<Home> {
TextEditingController keyInputController = new TextEditingController();
TextEditingController valueInputController = new TextEditingController();
late File jsonFile;
late Directory dir;
String fileName = 'myJsonFile.json';
bool fileExists = false;
// Si usas otra cosa Pon String, dynamic
late Map<String, dynamic> fileContent;
@override
void initState() {
super.initState();
getApplicationDocumentsDirectory().then((Directory directory) {
dir = directory;
jsonFile = new File(dir.path '/' fileName);
jsonFile.createSync();
fileExists = jsonFile.existsSync();
print(fileExists);
if (fileExists) {
fileContent = jsonDecode(jsonFile.readAsStringSync());
setState(() {});
}
});
}
@override
void dispose() {
keyInputController.dispose();
valueInputController.dispose();
super.dispose();
}
void createFile(Map<String, String> content, Directory dir, String fileName) {
print('Creating file!');
File file = new File(dir.path '/' fileName);
file.createSync();
fileExists = true;
file.writeAsStringSync(json.encode(content));
}
void writeToFile(String key, String value) {
print('Writing to file');
Map<String, String> content = {key: value};
if (fileExists) {
print('File exists');
Map<String, String> jsonFileContent =
json.decode(jsonFile.readAsStringSync());
jsonFileContent.addAll(content);
jsonFile.writeAsStringSync(json.encode(jsonFileContent));
} else {
print('File does not exists!');
createFile(content, dir, fileName);
}
this.setState(() {
fileContent = json.decode(jsonFile.readAsStringSync());
});
}
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("JSON Tutorial"),
),
body: new Column(
children: <Widget>[
new Padding(padding: new EdgeInsets.only(top: 10.0)),
new Text(
"File content: ",
style: new TextStyle(fontWeight: FontWeight.bold),
),
new Text((fileContent.toString() == '')
? 'PlaceHolder'
: fileContent.toString()),
new Padding(padding: new EdgeInsets.only(top: 10.0)),
new Text("Add to JSON file: "),
new TextField(
controller: keyInputController,
),
new TextField(
controller: valueInputController,
),
new Padding(padding: new EdgeInsets.only(top: 20.0)),
new ElevatedButton(
child: new Text("Add key, value pair"),
onPressed: () {
writeToFile(keyInputController.text, valueInputController.text);
})
],
),
);
}
}
Есть идеи, что может случиться?
Ответ №1:
Во-первых, вы создаете файл
dir = directory;
jsonFile = new File(dir.path '/' fileName);
jsonFile.createSync();
затем убедитесь, что он был создан
fileExists = jsonFile.existsSync();
print(fileExists);
if (fileExists) {
...
}
И тогда, если это было так, вы читаете его содержимое и анализируете его как JSON
fileContent = jsonDecode(jsonFile.readAsStringSync());
setState(() {});
Таким образом, файл, который вы только что создали, пуст, и поэтому вы читаете пустой файл и передаете пустую строку в функцию jsonDecode. что приводит к ошибке
Вы также объявляете эту функцию, но никогда не вызываете ее:
void createFile(Map<String, String> content, Directory dir, String fileName) {
print('Creating file!');
File file = new File(dir.path '/' fileName);
file.createSync();
fileExists = true;
file.writeAsStringSync(json.encode(content));
}
Так что я предполагаю, что на самом деле вы хотели сделать что-то вроде этого:
@override
void initState() {
super.initState();
getApplicationDocumentsDirectory().then((Directory directory) {
dir = directory;
createFile(content, dir, fileName);
print(fileExists);
if (fileExists) {
fileContent = jsonDecode(jsonFile.readAsStringSync());
setState(() {});
}
});
}
Кроме того, метод CreateFile использует только поля класса, поэтому я не думаю, что вам не нужны какие-либо аргументы для него.
Комментарии:
1. Вы правы, но я использую CreateFile() в другой функции writeToFile, чтобы файл создавался, если он не существует, возможно, это избыточность, так как файл создан, но он пуст, как вы сказали, но это только для того, чтобы гарантировать мне будущий сбой. Я попробовал изменить то, о чем вы мне сказали, но оно осталось прежним. И вы правы в том, что метод CreateFile использует только поля класса, но функция, хотя и является частной, я хочу иметь возможность повторно использовать ее в будущем, поэтому я привел аргументы. Спасибо за вашу помощь, в конце концов, я не смог это исправить, я буду продолжать пытаться.
2. Также я попробовал это условие, чтобы просто не передавать ничего пустого в jsonDecode ():
fileContent = jsonDecode (jsonFile.readAsStringSync () == ''? "{'key': 'value'}": jsonFile.readAsStringSync ());
Но это все равно то же самое. Я даже попытался записать в файл, как только он был создан:jsonFile = new File (dir.path '/' fileName); jsonFile.createSync (); fileExists = jsonFile.existsSync(); writeToFile ('key', 'value');
но он все равно не работает, похоже, что ошибка в функции декодирования.
Ответ №2:
Ладно, я уже нашел свою ошибку, основная проблема в том, что этот код был с версией Dart 1, и при попытке адаптировать его к Dart 2 я обнаружил проблему. Что произошло, так это то, что при создании json-файла он пуст, так как в нем нет никакого ключа, значения, кажется, что Map<String, dynamic>
генерируется a, так как в одном из тестов, которые я делал, он выдал мне эту ошибку :
The following _TypeError was thrown building Home(dirty, state: HomeState # 5f124):
type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'Map<String, String>' of 'function result'
Поэтому я решил изменить свой файловый контент с этого:
late Map<String, String> fileContent;
к этому:
late Map fileContent;
Затем я решил изменить всю логику, чтобы оставить все как есть:
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
void main() {
runApp(new MaterialApp(
home: new Home(),
));
}
class Home extends StatefulWidget {
@override
State createState() => new HomeState();
}
class HomeState extends State<Home> {
TextEditingController keyInputController = new TextEditingController();
TextEditingController valueInputController = new TextEditingController();
late File jsonFile;
late Directory directory;
bool fileExists = false;
late Map fileContent;
@override
void initState() {
super.initState();
getApplicationDocumentsDirectory().then((dir) async {
directory = dir;
jsonFile = File('${directory.path}/juegos.json');
await jsonFile.create();
fileExists = await jsonFile.exists();
print(fileExists);
if (fileExists) {
if (await jsonFile.readAsString() == '') {
fileContent = {};
} else {
fileContent = jsonDecode(await jsonFile.readAsString());
}
setState(() {});
}
});
}
createFile() async {
jsonFile = File('${directory.path}/juegos.json');
await jsonFile.create();
}
writeToFile(String key, String value) async {
if (fileExists) {
if (fileContent.containsKey(key)) {
fileContent.update(key, (v) => v = value);
} else {
fileContent.putIfAbsent(key, () => value);
}
jsonFile.writeAsString(jsonEncode(fileContent));
} else {
createFile();
}
fileContent = jsonDecode(await jsonFile.readAsString());
}
@override
void dispose() {
keyInputController.dispose();
valueInputController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("JSON Tutorial"),
),
body: new Column(
children: <Widget>[
new Padding(padding: new EdgeInsets.only(top: 10.0)),
new Text(
"File content: ",
style: new TextStyle(fontWeight: FontWeight.bold),
),
new Text((fileExists) ? fileContent.toString() : 'Esta vacio'),
new Padding(padding: new EdgeInsets.only(top: 10.0)),
new Text("Add to JSON file: "),
new TextField(
controller: keyInputController,
),
new TextField(
controller: valueInputController,
),
new Padding(padding: new EdgeInsets.only(top: 20.0)),
new ElevatedButton(
child: new Text("Add key, value pair"),
onPressed: () {
writeToFile(keyInputController.text, valueInputController.text);
setState(() {});
})
],
),
);
}
}
Так как файл был пуст, необходимо было поставить условие, если он был пуст, что fileContent равен пустой карте, а не равен полному пустому …
И в моем файле wirteToFile я просто проверяю, что если у fileContent уже есть ключ, который я хочу записать, я обновляю или добавляю его, наконец, я снова обновляю fileContent.
В любом случае, теперь fileContent никогда не бывает пустым. что это была ошибка, так как он пытался прочитать что-то там, где не было абсолютно ничего.
Надеюсь, это кому-то поможет, и спасибо за помощь @h8moss, в конце концов, он был прав, и это было потому, что файл был пуст, хотя его предложение об изменении мне не помогло, единственное, что мне нужно было сделать, это убедиться, что он не был пустым, и если это было так, заполните его, по крайней мере, пустой картой. Так что еще раз спасибо.