Panduan Pasukan Lucee MY

JSON dalam CFML: serializeJSON dan kesilapan biasa

Cara guna serializeJSON dan deserializeJSON dalam Lucee CFML, perangkap kunci struct menjadi huruf besar, format query, nombor besar, tarikh dan ralat biasa.

JSON dalam CFML: serializeJSON dan kesilapan biasa
0
Kandungan

serializeJSON menukar nilai CFML kepada rentetan JSON dan deserializeJSON membalikkannya. Kedua-duanya nampak mudah, tetapi Lucee menyimpan perangkap yang menggigit klien JavaScript. Yang paling kerap: kunci struct yang ditulis tanpa tanda petik menjadi HURUF BESAR dalam output JSON, jadi pengguna.namaPertama di JavaScript menjadi pengguna.NAMAPERTAMA. Artikel ini menjelaskan setiap perangkap dengan kod yang telah dijalankan pada Lucee 7.1 dan lulus.

serializeJSON dan deserializeJSON dalam satu minit

serializeJSON mengambil sebarang nilai CFML dan mengembalikan rentetan JSON; deserializeJSON melakukan sebaliknya, menjadikan struct atau tatasusunan. Contoh paling asas:

<cfscript>
    pengguna = { nama: "Ali", umur: 30, aktif: true };
    tulis = serializeJSON(pengguna);
    balik = deserializeJSON(tulis);
    writeOutput(tulis);
</cfscript>

Outputnya {"UMUR":30,"NAMA":"Ali","AKTIF":true}. Kunci menjadi huruf besar walaupun ditulis huruf kecil, dan susunan kunci tidak dijamin mengikut susunan sisipan, tetapi nilai boolean dan nombor kekal betul sebagai jenis JSON. Perkara pertama itulah perangkap paling kerap dilaporkan.

Perangkap nombor satu: kunci struct jadi huruf besar

Lucee menukar kunci struct yang ditulis tanpa tanda petik kepada huruf besar pada masa kompilasi, bukan serialisasi, supaya carian kunci menjadi pantas dan tidak sensitif huruf. Apabila serializeJSON membacanya, ia sudah pun huruf besar.

<cfscript>
    pengguna = { namaPertama: "Ali", umur: 30 };
    writeOutput(serializeJSON(pengguna));
    // {"UMUR":30,"NAMAPERTAMA":"Ali"}
</cfscript>

Ada tiga cara untuk mengekalkan huruf kunci. Cara paling bersih ialah menulis kunci dalam tanda petik, kerana kunci bertanda petik mengekalkan kes hurufnya:

<cfscript>
    pengguna = { "namaPertama": "Ali", "umur": 30 };
    writeOutput(serializeJSON(pengguna));
    // {"umur":30,"namaPertama":"Ali"}
</cfscript>

Cara kedua ialah arahan pengkompil preserveCase, yang menukar tingkah laku untuk seluruh templat. Ini yang biasanya orang maksudkan dengan preserveKeyCase, tetapi nama tetapan sebenar dalam pentadbir ialah "Preserve case for Struct keys", yang mengubah arahan pengkompil dot-notation-upper-case kepada false.

<cfprocessingdirective preserveCase="true">
<cfscript>
    pengguna = { namaPertama: "Ali", umur: 30 };
    writeOutput(serializeJSON(pengguna));
    // {"umur":30,"namaPertama":"Ali"}
</cfscript>

Cara ketiga ialah tetapan aplikasi, tetapi arahnya mudah disalah faham. this.serialization.preserveCaseForStructKey (alias preserveKeyCase) berkelakuan songsang: lalai true hanya melindungi kunci bertanda petik, manakala false menjadikan SEMUA kunci huruf besar dan mengatasi arahan preserveCase. Ia bukan penyelesaian kepada kunci tanpa petik.

TetapanDi manaKesan sebenar
preserveCase (pengkompil dot-notation-upper-case)<cfprocessingdirective preserveCase="true"> atau pentadbirKunci tanpa petik kekal kes asalnya
preserveCaseForStructKey (alias preserveKeyCase)this.serialization dalam Application.cfcfalse menjadikan semua kunci huruf besar; true (lalai) hanya melindungi kunci bertanda petik

Pilihan penting serializeJSON

Tandatangan serializeJSON dalam Lucee 7.1 ialah serializeJSON(var, queryFormat, useSecureJSONPrefixOrCharset, compact). Empat pilihan ini merangkumi kebanyakan keperluan, dan deserializeJSON menambah satu lagi iaitu strictMapping.

PilihanJenisTujuan
queryFormat"row", "column" atau "struct"Menentukan bentuk query dalam JSON
useSecureJSONPrefixbooleanAwalan // untuk halang JSON hijacking
compactbooleanfalse memberi JSON berindentasi mudah dibaca
strictMappingboolean (argumen kedua deserializeJSON)Kawal sama ada bentuk query dibina semula sebagai query atau dibiarkan sebagai struct

useSecureJSONPrefix berguna apabila JSON dihidangkan terus kepada pelayar: true sebagai argumen ketiga memberi awalan // bagi menghalang pencurian JSON melalui tag skrip silang tapak. compact=false memberi output berindentasi untuk penyahpepijatan.

Query kepada JSON: tiga bentuk berbeza

Query ialah tempat perbezaan Lucee dan Adobe ColdFusion paling ketara. serializeJSON menyokong tiga bentuk melalui queryFormat, dan bentuk lalai bukan yang paling berguna untuk klien JavaScript.

<cfscript>
    q = queryNew("id,nama", "integer,varchar", [[1,"Ali"],[2,"Siti"]]);
    writeOutput("row: " & serializeJSON(q) & "<br>");
    writeOutput("column: " & serializeJSON(q, "column") & "<br>");
    writeOutput("struct: " & serializeJSON(q, "struct"));
</cfscript>
queryFormatBentuk JSON
row (lalai){"COLUMNS":["id","nama"],"DATA":[[1,"Ali"],[2,"Siti"]]}
column{"ROWCOUNT":2,"COLUMNS":["id","nama"],"DATA":{"ID":[1,2],"NAMA":["Ali","Siti"]}}
struct[{"id":1,"nama":"Ali"},{"id":2,"nama":"Siti"}]

Bentuk struct ialah yang paling mesra JavaScript kerana ia menghasilkan tatasusunan objek biasa, sama seperti API REST moden. Bentuk row dan column meniru format lama Adobe ColdFusion yang meletakkan nama lajur dalam COLUMNS dan data dalam DATA. Adobe turut menyokong queryFormat="struct" sejak CF11, tetapi dua perbezaan tinggal. Pertama, Lucee mengekalkan huruf nama lajur seperti yang ditakrifkan, manakala Adobe cenderung menormalkannya kepada huruf besar. Kedua, dalam bentuk column, kunci DATA Lucee tetap huruf besar (ID, NAMA) kerana melalui laluan kunci tanpa petik yang sama. Apabila membaca semula JSON bentuk column, deserializeJSON secara lalai membiarkannya sebagai struct, tetapi strictMapping=false membina semula ia menjadi query.

<cfscript>
    q = queryNew("id,nama", "integer,varchar", [[1,"Ali"],[2,"Siti"]]);
    jsonKolom = serializeJSON(q, "column");
    sPlain = deserializeJSON(jsonKolom);
    qBalik = deserializeJSON(jsonKolom, false);
    writeOutput("sPlain struct: " & isStruct(sPlain) & " | qBalik query: " & isQuery(qBalik));
</cfscript>

Outputnya sPlain struct: true | qBalik query: true. Gunakan queryFormat="struct" untuk API, dan strictMapping=false hanya apabila anda memerlukan objek query kembali.

Struct berjenis ordered lawan struct biasa

Struct biasa tidak menjanjikan susunan kunci: ia mengikut jadual cincang dalaman, bukan susunan anda memasukkannya. Ini boleh memecahkan klien yang bergantung pada susunan kunci. Untuk mengekalkan susunan sisipan, gunakan struct berjenis ordered:

<cfscript>
    o = structNew("ordered");
    o.b = 2; o.a = 1; o.c = 3;
    writeOutput(serializeJSON(o));
    // {"B":2,"A":1,"C":3}
</cfscript>

Struct ordered mengekalkan susunan b, a, c, manakala struct biasa boleh mengembalikan susunan lain. Perbezaan ini penting untuk API yang menandatangani badan JSON dan mengesahkan susunan kunci.

Nombor besar dan ketepatan

CFML menyimpan nombor sebagai apungan berkembar Java, dan JSON berkongsi had yang sama dengan JavaScript: integer selamat sehingga 9007199254740991. Di atas itu, ketepatan hilang. Contoh sebenar: serializeJSON(9007199254740993) mengembalikan 9007199254740992. Perpuluhan panjang turut dipotong, contohnya 1.234567890123456789 menjadi 1.234567890123.

Untuk ID pesanan atau nombor akaun yang besar, hantar sebagai rentetan, kerana serializeJSON tidak memberi amaran apabila ketepatan hilang.

Tarikh dalam JSON

CFML tidak mempunyai jenis tarikh dalam JSON, jadi serializeJSON menukar objek tarikh kepada rentetan ikut locale, bukan kepada format ISO 8601. serializeJSON(createDateTime(2026,10,7,14,30,0)) menghasilkan "October, 07 2026 14:30:00 +0800", yang sukar dihurai klien. Amalan terbaik ialah format tarikh sendiri sebelum serialisasi:

<cfscript>
    d = createDateTime(2026, 10, 7, 14, 30, 0);
    iso = dateFormat(d, "yyyy-mm-dd") & "T" & timeFormat(d, "HH:mm:ss");
    writeOutput(serializeJSON(iso));
    // "2026-10-07T14:30:00"
</cfscript>

Kitaran dan rujukan membulat

Struct yang merujuk dirinya sendiri tidak boleh diwakili dalam JSON. Lucee tidak membaling ralat, sebaliknya menukar medan membulat itu kepada null secara senyap, contohnya { "diri": null }. Klien menerima data yang kelihatan sah tetapi kehilangan medan. Pastikan graf objek bebas kitaran sebelum serialisasi.

Data bukan ASCII dan UTF-8

JSON menggunakan UTF-8, dan Lucee mengendalikannya dengan betul selagi fail sumber dan respons anda juga UTF-8. serializeJSON({ nama: "Siti Nurhaliza" }) menghasilkan {"NAMA":"Siti Nurhaliza"} tanpa aksara rosak. Masalah biasanya pada pengepala respons yang menetapkan charset lain, jadi nyatakan charset=utf-8.

Untuk membina titik akhir JSON, anda mesti menetapkan Content-Type kepada application/json sebelum menghantar badan. Tanpanya, pelayar dan klien menafsirkan badan sebagai HTML atau teks kosong.

<cfsetting enablecfoutputonly="true">
<cfheader name="X-Content-Type-Options" value="nosniff">
<cfcontent type="application/json; charset=utf-8" reset="true"><cfoutput>#serializeJSON({ status: "ok", nama: "Ali" })#</cfoutput>

cfcontent menghantar Content-Type: application/json;charset=utf-8 dan badan {"STATUS":"ok","NAMA":"Ali"}. enablecfoutputonly="true" menghalang ruang putih mencemari JSON, manakala X-Content-Type-Options: nosniff mengelakkan pelayar meneka jenis kandungan. Untuk API penuh, baca REST API dalam Lucee.

Membaca JSON daripada API luar

Arah sebaliknya ialah memanggil API luar dengan cfhttp dan menukar jawapannya. Perangkap utama: resp.statusCode dalam Lucee memulangkan "200 OK" dengan frasa sebab, bukan "200", jadi perbandingan terus dengan 200 gagal. Gunakan resp.responseHeader.status_code.

<cfscript>
    hasil = { ok: false, data: {}, ralat: "" };
    try {
        cfhttp(url = "https://httpbin.org/json", method = "GET", result = "resp", timeout = 10);
        if (resp.responseHeader.status_code == 200 && isJSON(resp.fileContent)) {
            hasil.data = deserializeJSON(resp.fileContent);
            hasil.ok = true;
        } else {
            hasil.ralat = "HTTP " & resp.responseHeader.status_code & " atau badan bukan JSON";
        }
    } catch (any e) {
        hasil.ralat = e.message;
    }
    writeOutput(hasil.ok & " | " & hasil.data.slideshow.title);
</cfscript>

Tiga lapisan pertahanan di sini: semakan kod status, isJSON sebelum deserializeJSON, dan blok try dan catch. Untuk butiran lanjut, rujuk cfhttp dan API luar. Untuk menyahpepijat ralat JSON, nyahpepijat CFML dalam VS Code membantu memeriksa nilai sebelum serialisasi. Fungsi struct berkaitan dibincangkan dalam fungsi struct CFML.

Soalan Lazim (FAQ)

Kenapa kunci struct saya jadi huruf besar dalam JSON?

Lucee menukar kunci struct tanpa tanda petik kepada huruf besar semasa kompilasi. Tulis kunci dalam tanda petik, gunakan <cfprocessingdirective preserveCase="true">, atau aktifkan "Preserve case for Struct keys" dalam pentadbir Lucee.

Bentuk query mana yang patut saya guna untuk API?

Guna queryFormat="struct" kerana ia menghasilkan tatasusunan objek biasa yang paling mudah dihurai JavaScript, berbanding bentuk row dan column yang menggunakan kunci COLUMNS dan DATA.

Kenapa nombor besar saya berubah selepas serializeJSON?

CFML menyimpan nombor sebagai apungan berkembar, dengan integer selamat hanya sehingga 9007199254740991. Di atas itu ketepatan hilang. Hantar nilai besar sebagai rentetan.

Bagaimana saya menghantar JSON daripada halaman CFML?

Guna cfcontent type="application/json; charset=utf-8" sebelum menulis badan, dan hidupkan enablecfoutputonly="true" supaya tiada ruang putih mencemari output.

Apa bezanya Lucee dan Adobe ColdFusion untuk JSON?

Kedua-duanya menyokong queryFormat="struct" sejak CF11, tetapi Lucee menukar kunci struct tanpa petik kepada huruf besar secara lalai manakala Adobe cenderung mengekalkan kes asalnya. Ini punca utama JSON yang kelihatan berbeza antara dua enjin.

Sumber

  • Dokumentasi serializeJSON Lucee: https://docs.lucee.org/reference/functions/serializejson.html
  • Dokumentasi deserializeJSON Lucee: https://docs.lucee.org/reference/functions/deserializejson.html
  • Resipi Lucee tentang pengekalan kes kunci struct: https://docs.lucee.org/recipes/structs-preserve-key-case.html
  • Rujukan serializeJSON Adobe ColdFusion: https://cfdocs.org/serializejson

Senarai semak

  • Gunakan queryFormat="struct" untuk API supaya klien menerima tatasusunan objek.
  • Tulis kunci struct dalam tanda petik atau aktifkan preserveCase untuk elak kunci huruf besar.
  • Hantar nombor besar dan ID sebagai rentetan, bukan nombor.
  • Format tarikh kepada ISO 8601 sendiri sebelum serializeJSON.
  • Pastikan graf objek bebas kitaran supaya tiada medan hilang menjadi null.
  • Tetapkan Content-Type: application/json; charset=utf-8 pada setiap respons JSON.
  • Semak resp.responseHeader.status_code, bukan resp.statusCode, selepas cfhttp.
  • Sahkan badan dengan isJSON sebelum deserializeJSON, dan bungkus dalam try dan catch.
Bab 8 daripada 10

Asas CFML untuk Pemula

Log masuk untuk menyimpan kemajuan bacaan anda.

0 komen

0 komen

Log masuk untuk menjawab atau memberi komen.

Log masuk