Flutter: JSON Parser example & CROS issue
Flutter的Json處理真是有夠難...
看了老半天還是看不太懂, 挫折感重重.
他有兩套方法一個是dart內建的dart:convert
,另一個是套件,其號稱較容易處理複雜的JSON結構.
===
一個外國網友說:
I have to admit, I was missing the gson world of Android after working with JSON in Flutter/Dart. When I started working with APIs in Flutter, JSON parsing really had me struggle a lot. And I’m certain, it confuses a lot of you beginners.
by Pooja Bhaumik
大家可以去讀這篇json parse in flutter文章
ps:額外閱讀:30天Flutter手滑系列 - JSON與序列化(JSON and serialization)
---
Is there a GSON/ Jackson/ Moshi equivalent in Flutter?
The simple answer is no.
似乎是因為 程式庫 反射(run-time reflection)的關係,使得Flutter 無法對其做檔案最小化.
----
在android我們用gson太久,太方便,所以被寵壞了.
一換到flutter後,沒有gson,我們就不會用了.
寫程式這種技能,就跟啃饅頭一樣,很簡單,但你還得一口一口慢慢啃完.
對,沒有自己寫出一段json parser範例code,你就是算不會他.
一定要自己寫出來,結果有跑出來才算,然後在熟習其用法.等到真實專案遇到了就不會害怕.
---
Flutter有內建 dart:convert
library可用來parse JSON. "為較小的項目使用手動序列化數據",但這是一個很基本的library.
Basic JSON serialization in Flutter is very simple. Flutter has a built-in
dart:convert
library that includes a straightforward JSON encoder and
decoder.
JSON 解碼是指在 dart:convert
中使用內置的 JSON 解碼器。它包括將原始 JSON 字符串傳遞給 jsonDecode()
方法,然後在產生的 Map<String, dynamic>
計算結果中尋找你需要的值。它沒有外部依賴或者特定的設置過程,這有利於快速證明概念。
當你的項目變大時,手動解碼表現得並不理想。手動編寫解碼邏輯會變得難以管理並容易出錯。如果你產生了筆誤去獲取一個不存在的 JSON 字段,你的代碼會在運行時拋出一個錯誤。
----
轉換json (json serialization) 需要建立一個model class.
這很繁瑣,以前在設計原生android時,我們不需要自己寫這model class,因為有plug-in可以自動產生.所以很快可以產生,但flutter裡有沒有?
有的:
使用代碼生成庫序列化 JSON 數據
json_serializable
,一個自動化源代碼生成器來為你生成 JSON 序列化數據模板。
寫到這我就有感而發,軟體工程師必須要專注生產,遇到這麼複雜不熟悉的工作,很容易就放棄.一家公司,如果不重視軟體研發,視軟體研發是草芥一般的話,
工程師很容易就離職,公司所累積的技術就流失了.
----
在前景轉換Json時不能超過16ms. 否則須在背景處理.
當你需要進行一個非常複雜的計算時,例如解析一個巨大的 JSON 文件。
如果這項工作耗時超過了 16 毫秒,那麼你的用戶就會感受到停頓。16ms有多短,就是0.016 second.也就是說超級短的,因此,除非自我測試時可以容忍外,其餘轉換json的工作勢必一律都得在背景處理了.
這就要牽涉到flutter中的isolate的用法.只有將這部分-轉換json的工作移交到單獨的 isolate中,把工作放在背景執行. 所以UI就不會lag了.(在android中就是AsyncTask).
----
大坑: DioError [DioErrorType.response]: XMLHttpRequest error.
不明原因遇到這問題,跟dio有關....似乎跟瀏覽器的跨域問題有關.
(現代瀏覽器支援在API 容器(如 XMLHttpRequest 或 Fetch )中使用CORS 以降低跨來源HTTP 請求的風險。 )
CORS issue
跨來源資源共用(Cross-Origin Resource Sharing (CORS))是一種使用額外 HTTP 標頭令目前瀏覽網站的使用者代理 (en-US)取得存取其他來源(網域)伺服器特定資源權限的機制。當使用者代理請求一個不是目前文件來源——例如來自於不同網域(domain)、通訊協定(protocol)或通訊埠(port)的資源時,會建立一個跨來源 HTTP 請求(cross-origin HTTP request)。舉個跨來源請求的例子:http://domain-a.com
HTML 頁面裡面一個 <img> 標籤的 src 屬性 (en-US)載入來自 http://domain-b.com/image.jpg
的圖片。現今網路上許多頁面所載入的資源,如 CSS 樣式表、圖片影像、以及指令碼(script)都來自與所在位置分離的網域,如內容傳遞網路(content delivery networks, CDN)。
header "Access-Control-Allow-Origin": "*"
且同樣的只有web上跑會有問題,在android上跑就ok,可見會出錯是瀏覽器環境的設定問題,而不是code有問題.
solution 1: (recommend)
start chrome without CROS code
1- Go to flutter\bin\cache and remove a file named: flutter_tools.stamp
2- Go to flutter\packages\flutter_tools\lib\src\web and open the file chrome.dart.
3- Find '--disable-extensions'
4- Add '--disable-web-security' // 也可以在加上 '--user-data-dir'
5. run pub get
他會出現:
Building flutter tool...
Running pub upgrade...
solution 2: (在test and debug時不能使用)
只有在上線後才可以用.
這個更方便,安裝chrome extension:
以後被檔住時,按下解鎖即可.
---------
若設定成功,dio error 執行錯誤將會不見, 在瀏覽器上取而代之的是一行警告,但這不影響執行結果.
-----
至於如何使用 , 我的pubspec.yaml設定如下
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^1.0.3
json_annotation:
json_serializable:
build_runner: ^2.0.6
dio: ^4.0.0
module:
androidX: true # Add this line.
-----
然後其他請按照官方說明執行:
1.以json_serializable的方式創建model類
import 'package:json_annotation/json_annotation.dart';
// user.g.dart 將在我們運行生成命令後自動生成
part 'user.g.dart';
///這個標註是告訴生成器,這個類是需要生成Model類的
()
class User{
User(this.name, this.email);
String name;
String email;
//不同的類使用不同的mixin即可
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
Map<String, dynamic> toJson() => _$UserToJson(this);
}
有了這個設置,源碼生成器將生成用於序列化name
和email
字段的JSON代碼。
如果需要,自定義命名策略也很容易。例如,如果我們正在使用的API返回帶有_snake_case_的對象,但我們想在我們的模型中使用_lowerCamelCase_, 那麼我們可以使用@JsonKey標註:
/// Tell json_serializable that "registration_date_millis" should be
/// mapped to this property.
(name: 'registration_date_millis')
final int registrationDateMillis;
有了json_serializable
,我們可以在User
類上忘記任何手動的JSON序列化 。源代碼生成器創建一個名為user.g.dart
的文件,它具有所有必需的序列化邏輯。
json_serializable
第一次創建類時,您會看到類似的錯誤user.g.dart不存在
。
solution:
一次性生成:
flutter packages pub run build_runner build
通過在我們的項目根目錄下運行
持續生成
使用_watcher_可以使我們的源代碼生成的過程更加方便。它會監視我們項目中文件的變化,並在需要時自動構建必要的文件。我們可以通過
flutter packages pub run
build_runner watch
在項目根目錄下運行來啟動_watcher_。
只需啟動一次觀察器,然後並讓它在後台運行,這是安全的。
使用json_serializable模型
要通過json_serializable
方式反序列化JSON字符串,我們不需要對先前的代碼進行任何更改。
Map userMap = JSON.decode(json);
var user = new User.fromJson(userMap);
這方法相對好用
import 'package:json_annotation/json_annotation.dart';
part 'car.d.dart';
@JsonSerializable()
class Car {
String make;
String model;
// Default constructor
Car(this.make, this.model);
factory Car.fromJson(Map<String, dinamic> json) => _$CarFromJson(json);
Map<String, dynamic> toJson() => _$CarToJson(this);
}
上面紅字部分是重點,是根據你的model name來設定,例如這邊的model name是Car.
後面就
固定
加上____FromJson(json) 與 ____toJson().
更多詳細介紹: https://everyday.codes/mobile/everything-you-need-to-know-about-json-in-flutter/
留言
張貼留言