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 字段,你的代碼會在運行時拋出一個錯誤。

---- 

Serializing JSON inside model classes

轉換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);
}
 

有了這個設置,源碼生成器將生成用於序列化nameemail字段的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

通過在我們的項目根目錄下運行
我們可以為我們的model生成json序列化class.
 

持續生成

使用_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/
 

 

 

 

 


 

 

 

 

 

 

 


 

 

留言

這個網誌中的熱門文章

最爛的銀行服務-玉山銀行

更改google drive預設存放目錄位置Change Google Drive Default Folder Location in Windows

ios app 上架時app icon要注意事項

SMR疊瓦式hdd致命缺陷被解決????!!!

google play 正式發布前測試報告...非常好用.

關於google play console app應用程式簽署

舊有app在Android 12 閃退問題& app Splash screens

Mark App Design Apps - Terms and Privacy Policy (服務條款,隱私權政策)

寫APP真的是好工作嗎? Firebase host與我對Kotlin的感想

Google Play badge徽章產生器