Tag Archive json

JoGet: Bean Shell Create Json

วิธีดีที่สุดในการให้ข้อมูลจำนวนมากให้ javascript คือรูปแบบ json ไม่เว้นแม้แต่ใน JoGet เพราะถึงแม้จะสามารถดึงข้อมูลจาก input หรือ grid ได้ แต่มันจะถูกบันทึกลง database ไปด้วยแถมดูรก

วิธีที่ดีอีกวิธีคือให้ beanshell ใน section ดึงข้อมูลจาก query แล้วแสดงออกมาใน input ตัวเดียว

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.joget.apps.app.service.AppUtil;
import org.joget.apps.form.model.Element;
import org.joget.apps.form.model.FormData;
import org.joget.apps.form.model.FormRow;
import org.joget.apps.form.model.FormRowSet;
import org.joget.apps.form.service.FormUtil;
import org.joget.commons.util.LogUtil;
import org.json.simple.JSONObject;

public FormRowSet load(Element element, String username, FormData formData) {

 FormRowSet rows = new FormRowSet();

 Connection con = null;
 try {
  DataSource ds = (DataSource) AppUtil.getApplicationContext().getBean("setupDataSource");
  con = ds.getConnection();

  sql = "SELECT appId, appVersion, name FROM jwdb.app_app WHERE published = 1;";
  //LogUtil.info("Bean Shell Create Json", "sql = " + sql);

  if (!con.isClosed()) {
   PreparedStatement stmt = con.prepareStatement(sql);
   ResultSet rs = stmt.executeQuery();

   /* create json */
   JSONObject json = new JSONObject();
   while (rs.next()) {
    JSONObject item = new JSONObject();

    item.put("appVersion", rs.getObject("appVersion"));
    item.put("name", rs.getObject("name"));

    json.put(rs.getObject("appId"), item);
   }

   /* add json to input */
   FormRow row = new FormRow();
   rows.add(row);
   row.put("json", json.toString());
  }

 } catch (Exception e) {
  LogUtil.error(getClassName(), e, "Bean Shell Create Json");
 } finally {
  try {
   if (con != null) {
    con.close();
   }
  } catch (SQLException e) {}
 }

 return rows;
}

return load(element, primaryKey, formData);

ตัวอย่างฟอร์ม

{
    "className": "org.joget.apps.form.model.Form",
    "properties": {
        "noPermissionMessage": "",
        "loadBinder": {
            "className": "org.joget.apps.form.lib.WorkflowFormBinder",
            "properties": {}
        },
        "name": "Bean Shell Create Json",
        "description": "",
        "postProcessorRunOn": "both",
        "permission": {
            "className": "",
            "properties": {}
        },
        "id": "BeanShellCreateJson",
        "postProcessor": {
            "className": "",
            "properties": {}
        },
        "storeBinder": {
            "className": "org.joget.apps.form.lib.WorkflowFormBinder",
            "properties": {}
        },
        "tableName": "prototypes"
    },
    "elements": [
        {
            "elements": [
                {
                    "elements": [
                        {
                            "className": "org.joget.apps.form.lib.TextArea",
                            "properties": {
                                "readonly": "",
                                "validator": {
                                    "className": "",
                                    "properties": {}
                                },
                                "workflowVariable": "",
                                "id": "json",
                                "label": "JSON",
                                "placeholder": "",
                                "rows": "10",
                                "value": "",
                                "cols": "100",
                                "readonlyLabel": ""
                            }
                        }
                    ],
                    "className": "org.joget.apps.form.model.Column",
                    "properties": {
                        "width": "100%"
                    }
                }
            ],
            "className": "org.joget.apps.form.model.Section",
            "properties": {
                "readonly": "",
                "loadBinder": {
                    "className": "org.joget.apps.form.lib.BeanShellFormBinder",
                    "properties": {
                        "useAjax": "",
                        "cacheIdlePause": "120",
                        "cacheInterval": "",
                        "script": "import java.sql.Connection;\nimport java.sql.PreparedStatement;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport javax.sql.DataSource;\nimport org.joget.apps.app.service.AppUtil;\nimport org.joget.apps.form.model.Element;\nimport org.joget.apps.form.model.FormData;\nimport org.joget.apps.form.model.FormRow;\nimport org.joget.apps.form.model.FormRowSet;\nimport org.joget.apps.form.service.FormUtil;\nimport org.joget.commons.util.LogUtil;\nimport org.json.simple.JSONObject;\n\npublic FormRowSet load(Element element, String username, FormData formData) {\n\n FormRowSet rows = new FormRowSet();\n\n Connection con = null;\n try {\n  DataSource ds = (DataSource) AppUtil.getApplicationContext().getBean(\"setupDataSource\");\n  con = ds.getConnection();\n\n  sql = \"SELECT appId, appVersion, name FROM jwdb.app_app WHERE published = 1;\";\n  //LogUtil.info(\"Bean Shell Create Json\", \"sql = \" + sql);\n\n  if (!con.isClosed()) {\n   PreparedStatement stmt = con.prepareStatement(sql);\n   ResultSet rs = stmt.executeQuery();\n\n   /* create json */\n   JSONObject json = new JSONObject();\n   while (rs.next()) {\n    JSONObject item = new JSONObject();\n\n    item.put(\"appVersion\", rs.getObject(\"appVersion\"));\n    item.put(\"name\", rs.getObject(\"name\"));\n\n    json.put(rs.getObject(\"appId\"), item);\n   }\n\n   /* add json to input */\n   FormRow row = new FormRow();\n   rows.add(row);\n   row.put(\"json\", json.toString());\n  }\n\n } catch (Exception e) {\n  LogUtil.error(getClassName(), e, \"Bean Shell Create Json\");\n } finally {\n  try {\n   if (con != null) {\n    con.close();\n   }\n  } catch (SQLException e) {}\n }\n\n return rows;\n}\n\nreturn load(element, primaryKey, formData);"
                    }
                },
                "permissionReadonly": "",
                "permission": {
                    "className": "",
                    "properties": {}
                },
                "comment": "",
                "id": "section1",
                "label": "Bean Shell Create Json",
                "storeBinder": {
                    "className": "",
                    "properties": {}
                },
                "readonlyLabel": ""
            }
        }
    ]
}

JAVA: Create Json

การสร้าง json ใน java ทั้งแบบ array [“aaa”, “bbb”, “ccc”] และ object {“aaa”:”bbb”, “ccc”:”ddd”, “eee”:”fff”} ทำได้ง่ายๆ

สร้าง json array

import org.json.simple.JSONArray;

JSONArray json = new JSONArray();
while (rs.next()) {
JSONObject item = new JSONArray();

item.add(rs.getObject("appId"));
item.add(rs.getObject("appVersion"));
item.add(rs.getObject("name"));

json.add(item);
}

รวมร่าง

json.add( { JSONArray ที่ต้องการรวม } );

สร้าง json object

import org.json.simple.JSONObject;

JSONObject json = new JSONObject();
while (rs.next()) {
JSONObject item = new JSONObject();

item.put("appVersion", rs.getObject("appVersion"));
item.put("name", rs.getObject("name"));

json.put(rs.getObject("appId"), item);
}

รวมร่าง

json.put( {index ที่ต้องการ}, { JSONArray ที่ต้องการรวม } );

แปลงเป็น string เพื่อใช้อย่างอื่น

json.toString()

อ่านเพิ่มเติม JoGet: Bean Shell Create Json

javascript: string to JSON.parse()

หลังจากใช้ MySQL: return json format กันเป็นแล้วเรามาลองใช้กับ joget แทนที่จะต้องไปเขียน beanshell ให้ยุ่งยาก (พลาดง่ายๆ อีกตะหาก) ดูจริงๆ แล้วสามารถใช้กับหน้าเว็บทั้งหมดไม่ใช่แค่ joGet เพราะมันคือ javascript พื้นฐาน

เปิดฟอร์มที่ต้องการมา

  1. สร้าง section ขึ้นมา โดยกำหนด Load Binder เป็น JDBC Binder และ SQL SELECT Query ใส่ query ที่ต้องการ เช่น
    SELECT
        CONCAT('[',
                GROUP_CONCAT(JSON_OBJECT('label', name, 'value', appId)),
                ']') AS optionsList
    FROM
        jwdb.app_app
    ORDER BY name ASC
  2. ลาก Hidden Field มาตั้ง id เป็น optionsList ใน section ที่สร้างไว้
  3. เขียน javascript ดึงค่ามาจาก string json ที่เก็บไว้ใน input ชื่อ optionsList โดยใช้ JSON.parse() เผื่อความปลอดภัย ควรใช้ JSON.parse() ใน try catch เพราะว่าถ้า json ที่ได้ผิดปกติจะสามารถควบคุมได้ ลาก Custom HTML มาแล้วใส่ code
    <table class="table table-striped" id="tableA">
        <thead>
            <tr>
                <th scope="col" style="width:20px;">#</th>
                <th scope="col">Name</th>
                <th scope="col">Value</th>
            </tr>
        </thead>
        <tbody>
        </tbody>
    </table>
    <script>
        $(document).ready(function () {
    
            let html = new Array();
            let no = 0;
            let optionsList = $('#optionsList');
            try {
                let temp = JSON.parse(optionsList.val());
    
                if (temp.length > 0) {
                    $.each(temp, function (index, value) {
                        no++;
                        html.push('<tr><th scope="row">' + no + '</th><td>' + value.label + '</td><td>' + value.value + '</td><tr>');
                    });
                }
                if (html.length == 0) {
                    html = '<tr><th scope="row">-</th><td>-</td><td>-</td><tr>';
                } else {
                    html = html.join("\n");
                }
            } catch (e) {
                console.log(e.message);
                html = '<h1>Error!!</h1>';
            }
            $('#tableA > tbody').html(html);
    
        });
    </script>

สามารถ copy form ตัวอย่างได้จาก json

{
    "className": "org.joget.apps.form.model.Form",
    "properties": {
        "noPermissionMessage": "",
        "loadBinder": {
            "className": "org.joget.apps.form.lib.WorkflowFormBinder",
            "properties": {}
        },
        "name": "JSON Parse",
        "description": "",
        "postProcessorRunOn": "both",
        "permission": {
            "className": "",
            "properties": {}
        },
        "id": "JSONParse",
        "postProcessor": {
            "className": "",
            "properties": {}
        },
        "storeBinder": {
            "className": "org.joget.apps.form.lib.WorkflowFormBinder",
            "properties": {}
        },
        "tableName": "prototypes"
    },
    "elements": [
        {
            "elements": [
                {
                    "elements": [
                        {
                            "className": "org.joget.apps.form.lib.HiddenField",
                            "properties": {
                                "useDefaultWhenEmpty": "",
                                "workflowVariable": "",
                                "id": "optionsList",
                                "value": ""
                            }
                        }
                    ],
                    "className": "org.joget.apps.form.model.Column",
                    "properties": {
                        "width": "100%"
                    }
                }
            ],
            "className": "org.joget.apps.form.model.Section",
            "properties": {
                "readonly": "",
                "loadBinder": {
                    "className": "org.joget.plugin.enterprise.JdbcLoadBinder",
                    "properties": {
                        "jdbcDatasource": "default",
                        "sql": "SELECT \n    CONCAT('[',\n            GROUP_CONCAT(JSON_OBJECT('label', name, 'value', appId)),\n            ']') AS optionsList\nFROM\n    jwdb.app_app\nORDER BY name ASC"
                    }
                },
                "permissionReadonly": "",
                "permission": {
                    "className": "",
                    "properties": {}
                },
                "comment": "",
                "id": "section1",
                "label": "",
                "storeBinder": {
                    "className": "",
                    "properties": {}
                },
                "readonlyLabel": ""
            }
        },
        {
            "elements": [
                {
                    "elements": [
                        {
                            "className": "org.joget.apps.form.lib.CustomHTML",
                            "properties": {
                                "autoPopulate": "",
                                "id": "field2",
                                "label": "",
                                "value": "<table class=\"table table-striped\" id=\"tableA\">\n    <thead>\n        <tr>\n            <th scope=\"col\" style=\"width:20px;\">#</th>\n            <th scope=\"col\">Name</th>\n            <th scope=\"col\">Value</th>\n        </tr>\n    </thead>\n    <tbody>\n    </tbody>\n</table>\n<script>\n    $(document).ready(function () {\n\n        let html = new Array();\n        let no = 0;\n        let optionsList = $('#optionsList');\n        try {\n            let temp = JSON.parse(optionsList.val());\n\n            if (temp.length > 0) {\n                $.each(temp, function (index, value) {\n                    no++;\n                    html.push('<tr><th scope=\"row\">' + no + '</th><td>' + value.label + '</td><td>' + value.value + '</td><tr>');\n                });\n            }\n            if (html.length == 0) {\n                html = '<tr><th scope=\"row\">-</th><td>-</td><td>-</td><tr>';\n            } else {\n                html = html.join(\"\\n\");\n            }\n        } catch (e) {\n            console.log(e.message);\n            html = '<h1>Error!!</h1>';\n        }\n        $('#tableA > tbody').html(html);\n\n    });\n</script>"
                            }
                        }
                    ],
                    "className": "org.joget.apps.form.model.Column",
                    "properties": {
                        "width": "100%"
                    }
                }
            ],
            "className": "org.joget.apps.form.model.Section",
            "properties": {
                "readonly": "",
                "loadBinder": {
                    "className": "",
                    "properties": {}
                },
                "permissionReadonly": "",
                "permission": {
                    "className": "",
                    "properties": {}
                },
                "comment": "",
                "id": "section2",
                "label": "Options List",
                "storeBinder": {
                    "className": "",
                    "properties": {}
                },
                "readonlyLabel": ""
            }
        }
    ]
}

MySQL: return json format

mysql ก็ support json เพราะว่าเดี๋ยวนี้อะไรๆ ก็แลกเปลี่ยนข้อมูลกันโดยใช้ json ถ้าสามารถ query จาก mysql ให้ result ออกมาเป็น json เลย ก็สะดวกที่จะไม่ต้องใช้ java หรือ beanshell ใน joget แปลงผลลัพธ์ให้ยุ่งยาก แค่มี input ซักตัวก็สามารถนำมันไปใช้ได้เลย

เริ่มจากการที่เขียน query ตามปกติเพื่อเลือกข้อมูลที่ต้องการ เช่น

SELECT
    *
FROM
    snippets.province
ORDER BY PROVINCE_NAME ASC;

จะเห็นว่ามี column PROVINCE_ID, PROVINCE_CODE, PROVINCE_NAME และ GEO_ID ทีนี้จะแปลง ให้เป็นรูปแบบ json array โดยใช้ JSON_OBJECT วิธีการใช้คือ

SELECT JSON_OBJECT('name1', value1, 'name2', value2, 'name3', value3);

คือ

SELECT
    JSON_OBJECT('PROVINCE_ID',
            PROVINCE_ID,
            'PROVINCE_CODE',
            PROVINCE_CODE,
            'PROVINCE_NAME',
            PROVINCE_NAME,
            'GEO_ID',
            GEO_ID) AS json
FROM
    snippets.province
ORDER BY PROVINCE_NAME ASC;

ค่าค่อยดูเป็น json ขึ้นมาหน่อย แต่มันยังอยู่คนละแถว ไม่เป็นไรมีตัวช่วย

SELECT
    CONCAT('[',
            GROUP_CONCAT(JSON_OBJECT('PROVINCE_ID',
                        PROVINCE_ID,
                        'PROVINCE_CODE',
                        PROVINCE_CODE,
                        'PROVINCE_NAME',
                        PROVINCE_NAME,
                        'GEO_ID',
                        GEO_ID)),
            ']') AS json
FROM
    snippets.province
ORDER BY PROVINCE_NAME ASC;

แค่นี้ก็ได้ result เป็น json สมใจ อ่านตัวอย่างการใช้ได้ที่ javascript: string to JSON.parse()

MySQL: select ข้อมูลจาก json array

Mysql การดึงข้อมูลจากรูปแบบ json ที่เก็บเป็น array ไว้ จะเขียน query ซับซ้อนกว่าปกตินิดหนึ่งเพราะว่าต้องมีการดึงข้อมูลโดยทราบจำนวนแถว เช่น จากข้อมูล

CREATE TABLE `app_fd_books` (
  `id` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `dateCreated` datetime DEFAULT NULL,
  `dateModified` datetime DEFAULT NULL,
  `c_books` longtext COLLATE utf8_unicode_ci DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

INSERT INTO `app_fd_books` (`id`,`dateCreated`,`dateModified`,`c_books`) VALUES ('b04469bd-ac1430f1-12bd2000-a4f7da3a','2018-03-03 22:54:56','2018-03-03 22:54:56','[{\n		\"title\": \"สูญสิ้นความเป็นคน\",\n		\"author\": \"ดะไซ โอซามุ\",\n		\"publisher\": \"JLIT\"\n	}, {\n		\"title\": \"ถอดรหัสรักออนไลน์\",\n		\"author\": \"Aziz Ansari\",\n		\"publisher\": \"openworlds\"\n	},\n	{\n		\"title\": \"เพลงรัตติกาลในอินเดีย\",\n		\"author\": \"Antonio Tabucchi\",\n		\"publisher\": \"อ่านอิตาลี\"\n	},\n	{\n		\"title\": \"อันเดอร์กราวด์\",\n		\"author\": \"ผู้เขียน: ฮารูกิ มูราคามิ\",\n		\"publisher\": \"กำมะหยี่\"\n	},\n	{\n		\"title\": \"ร้านหนังสือเลขที่ 84 ถนนแชริงครอสส์\",\n		\"author\": \"Helene Hanff\",\n		\"publisher\": \"Bookmoby Press\"\n	},\n	{\n		\"title\": \"ปรัชญาในภาพยนตร์\",\n		\"author\": \"ผู้เขียน: สิตางค์ เจริญวงศ์ และ ฐานชน จันทร์เรือง\",\n		\"publisher\": \"ปล่อยสำนักพิมพ์\"\n	},\n	{\n		\"title\": \"ผู้มาเยือนหลังเที่ยงคืน\",\n		\"author\": \"แพทริก เนส\",\n		\"publisher\": \"เวิร์ดส์วอนเดอร์\"\n	},\n	{\n		\"title\": \"เข้าป่าหาชีวิต\",\n		\"author\": \"จอน คราคาวเออร์\",\n		\"publisher\": \"มูลนิธิหนังสือเพื่อสังคม\"\n	},\n	{\n		\"title\": \"ไกลกว่ารั้วบ้านของเรา\",\n		\"author\": \"โรสนี นูรฟารีดา\",\n		\"publisher\": \"ผจญภัย\"\n	},\n	{\n		\"title\": \"สัจนิยมมหัศจรรย์\",\n		\"author\": \"ชูศักดิ์ ภัทรกุลวณิชย์\",\n		\"publisher\": \"อ่าน\"\n	},\n	{\n		\"title\": \"เข้าป่าหาชีวิต\",\n		\"author\": \"จอน คราคาวเออร์\",\n		\"publisher\": \"มูลนิธิหนังสือเพื่อสังคม\"\n	},\n	{\n		\"title\": \"ความลับ 5 ข้อที่คุณต้องค้นให้พบก่อนตาย\",\n		\"author\": \"จอห์น อิซโซ\",\n		\"publisher\": \"โอ้มายก้อด\"\n	},\n	{\n		\"title\": \"ฝนบางหยด กลายเป็นผีเสื้อ\",\n		\"author\": \"ลัดดา สงกระสินธิ์\",\n		\"publisher\": \"สมมติ\"\n	},\n	{\n		\"title\": \"Open Diary\",\n		\"author\": \"วรพจน์ พันธุ์พงศ์\",\n		\"publisher\": \"บางลำพู\"\n	},\n	{\n		\"title\": \"A Gothic Soul\",\n		\"author\": \"Jiří Karásek ze Lvovic\",\n		\"publisher\": \"Twisted Spoon\"\n	},\n	{\n		\"title\": \"ไกลกว่ารั้วบ้านของเรา\",\n		\"author\": \"โรสนี นูรฟารีดา\",\n		\"publisher\": \"ผจญภัย\"\n	},\n	{\n		\"title\": \"ประวัติศาสตร์นับศูนย์: สู่การสูญพันธุ์ครั้งที่ 6\",\n		\"author\": \"เอลิซาเบธ โคลเบิร์ต\",\n		\"publisher\": \"openworlds\"\n	},\n	{\n		\"title\": \"FUTURE : ปัญญาอนาคต\",\n		\"author\": \"ภิญโญ ไตรสุริยธรรมา\",\n		\"publisher\": \"openbooks\"\n	}\n]');

c_books มีจำนวนข้อมูลที่เก็บเอาไว้ 18 รายการ

เพื่อความสดวกการเขียน query จะใช้วิธีสร้างตารางเก็บ index ไว้ให้เกาะโดย เราจะสร้างตาราง integers ที่เก็บจำนวนเต็มตั้งแต่ 0 ถึง n ( function JSON_EXTRACT เริ่มจาก index 0 ) เอาไว้ให้ใช้ง่ายๆ เช่น

CREATE TABLE `integers` (
`row_number` int(4) NOT NULL,
PRIMARY KEY (`row_number`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO integers (row_number) VALUES ('0') , ('1') , ('2') , ('3') , ('4') , ('5') , ('6') , ('7') , ('8') , ('9') , ('10') , ('11') , ('12') , ('13') , ('14') , ('15') , ('16') , ('17') , ('18') , ('19') , ('20') , ('21') , ('22') , ('23') , ('24') , ('25') , ('26') , ('27') , ('28') , ('29') , ('30') , ('31') , ('32') , ('33') , ('34') , ('35') , ('36') , ('37') , ('38') , ('39') , ('40') , ('41') , ('42') , ('43') , ('44') , ('45') , ('46') , ('47') , ('48') , ('49') , ('50') , ('51') , ('52') , ('53') , ('54') , ('55') , ('56') , ('57') , ('58') , ('59') , ('60') , ('61') , ('62') , ('63') , ('64') , ('65') , ('66') , ('67') , ('68') , ('69') , ('70') , ('71') , ('72') , ('73') , ('74') , ('75') , ('76') , ('77') , ('78') , ('79') , ('80') , ('81') , ('82') , ('83') , ('84') , ('85') , ('86') , ('87') , ('88') , ('89') , ('90') , ('91') , ('92') , ('93') , ('94') , ('95') , ('96') , ('97') , ('98') , ('99') , ('100')

ส่วนเรื่องจะดึงข้อมูลถึงอันดับที่เท่าไหร่ ถ้าไม่บันทึกไว้ที่คอลัมน์อื่น ก็ทำได้โดยการเขียนเงื่อนไขเช่น ข้อมูลเกี่ยวกับหนังสือทุกเล่ม ต้องมีชื่อเรื่อง (title) ก็จะเขียนประมาณ

AND (SELECT
            TRIM(BOTH '"' FROM JSON_EXTRACT(datas.c_books,
                            CONCAT('$[', row_number, '].title')))
        FROM
            app_fd_books
        WHERE
            id = 'b04469bd-ac1430f1-12bd2000-a4f7da3a') IS NOT NULL

ตัวอย่าง query ที่เขียนเสร็จแล้ว เช่น

SELECT
    row_number,
    TRIM(BOTH '"' FROM JSON_EXTRACT(datas.c_books,
                CONCAT('$[', row_number, '].title'))) AS title,
    TRIM(BOTH '"' FROM JSON_EXTRACT(datas.c_books,
                CONCAT('$[', row_number, '].author'))) AS author,
    TRIM(BOTH '"' FROM JSON_EXTRACT(datas.c_books,
                CONCAT('$[', row_number, '].publisher'))) AS publisher
FROM
    app_fd_books AS datas,
    (SELECT
        row_number AS row_number
    FROM
        integers) AS length
WHERE
    datas.id = 'b04469bd-ac1430f1-12bd2000-a4f7da3a'
        AND (SELECT
            TRIM(BOTH '"' FROM JSON_EXTRACT(datas.c_books,
                            CONCAT('$[', row_number, '].title')))
        FROM
            app_fd_books
        WHERE
            id = 'b04469bd-ac1430f1-12bd2000-a4f7da3a') IS NOT NULL
ORDER BY row_number;

แต่เมื่อใช้ไปนานๆ เจอวิธีที่เขียนได้ง่ายกว่าเดิมคือใช้ JSON_LENGTH ก็จะได้รูปแบบ

SELECT
    row_number,
    TRIM(BOTH '"' FROM JSON_EXTRACT(datas.c_books,
                CONCAT('$[', row_number, '].title'))) AS title,
    TRIM(BOTH '"' FROM JSON_EXTRACT(datas.c_books,
                CONCAT('$[', row_number, '].author'))) AS author,
    TRIM(BOTH '"' FROM JSON_EXTRACT(datas.c_books,
                CONCAT('$[', row_number, '].publisher'))) AS publisher
FROM
    integers AS i,
    app_fd_books AS datas
WHERE
    datas.id = 'b04469bd-ac1430f1-12bd2000-a4f7da3a'
        AND i.row_number < JSON_LENGTH(datas.c_books)
ORDER BY row_number;

มันง่าย และสั้นกว่าเยอะเลยพิชญ์

PHP: json_encode ไม่มีผลลัพธ์

เจอว่า json_encode มันไม่ return ผลลัพธ์ ออกมาเลยในหน้าเดียวกัน แต่ต่างกันที่ product id ลอง print_r / var_dump ตัว array ที่เข้าไปใน function ก็ปกติดี ไม่ได้มากมาย หรือมีเครื่องหมายพิเศษอะไร ลอง debug ดูถึงเจอว่าถ้าใช้ function json_last_error() มันจะ return เลข 7 กลับมาในหน้าที่มีปัญหา ลองค้นดูรหัส 7 คือ infinity number ลอง unset ตัว key ที่เป็น float(-INF) ดูก็กลับเป็นปกติจริงๆ

รหัสต่างๆ คือ

JSON error codes
Code Constant Meaning Availability
0 JSON_ERROR_NONE No error has occurred  
1 JSON_ERROR_DEPTH The maximum stack depth has been exceeded  
2 JSON_ERROR_STATE_MISMATCH Invalid or malformed JSON  
3 JSON_ERROR_CTRL_CHAR Control character error, possibly incorrectly encoded  
4 JSON_ERROR_SYNTAX Syntax error  
5 JSON_ERROR_UTF8 Malformed UTF-8 characters, possibly incorrectly encoded PHP 5.3.3
6 JSON_ERROR_RECURSION One or more recursive references in the value to be encoded PHP 5.5.0
7 JSON_ERROR_INF_OR_NAN One or moreNANor INFvalues in the value to be encoded PHP 5.5.0
8 JSON_ERROR_UNSUPPORTED_TYPE A value of a type that cannot be encoded was given PHP 5.5.0
9 JSON_ERROR_INVALID_PROPERTY_NAME A property name that cannot be encoded was given PHP 7.0.0
10 JSON_ERROR_UTF16 Malformed UTF-16 characters, possibly incorrectly encoded PHP 7.0.0
<!doctype html>
<html>
   <head>
      <meta charset="utf-8">
      <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
      <title>PHP: json_encode error</title>
      <meta content="Pitt Phunsanit" name="author" />
   </head>
   <body>
      <div class="container">
         <?php
            $datas = [
            	'ininfity' => -9e1000,
            	'title' => 'title',
            ];
            ?>
         <div class="row"><label class="col-md-2" for="">Datas Arrya:</label><textarea class="col-md-10" cols="100" rows="6"><?=var_dump($datas); ?></textarea></div>
         <div class="row"><label class="col-md-2" for="">Json Datas:</label><textarea class="col-md-10" cols="100" rows="6"><?=json_encode($datas); ?></textarea></div>
         <div class="row">
            <label class="col-md-2" for="">Json Error:</label>
            <div class="col-md-10"><?=json_last_error(); ?></div>
         </div>
         <table class="table table-striped">
            <caption><strong>JSON error codes</strong></caption>
            <thead>
               <tr>
                  <th>Code</th>
                  <th>Constant</th>
                  <th>Meaning</th>
                  <th>Availability</th>
               </tr>
            </thead>
            <tbody class="tbody">
               <tr>
                  <td><strong><code><?=JSON_ERROR_NONE; ?></code></strong></td>
                  <td><strong><code>JSON_ERROR_NONE</code></strong></td>
                  <td>No error has occurred</td>
                  <td class="empty">&nbsp;</td>
               </tr>
               <tr>
                  <td><strong><code><?=JSON_ERROR_DEPTH; ?></code></strong></td>
                  <td><strong><code>JSON_ERROR_DEPTH</code></strong></td>
                  <td>The maximum stack depth has been exceeded</td>
                  <td class="empty">&nbsp;</td>
               </tr>
               <tr>
                  <td><strong><code><?=JSON_ERROR_STATE_MISMATCH; ?></code></strong></td>
                  <td><strong><code>JSON_ERROR_STATE_MISMATCH</code></strong></td>
                  <td>Invalid or malformed JSON</td>
                  <td class="empty">&nbsp;</td>
               </tr>
               <tr>
                  <td><strong><code><?=JSON_ERROR_CTRL_CHAR; ?></code></strong></td>
                  <td><strong><code>JSON_ERROR_CTRL_CHAR</code></strong></td>
                  <td>Control character error, possibly incorrectly encoded</td>
                  <td class="empty">&nbsp;</td>
               </tr>
               <tr>
                  <td><strong><code><?=JSON_ERROR_SYNTAX; ?></code></strong></td>
                  <td><strong><code>JSON_ERROR_SYNTAX</code></strong></td>
                  <td>Syntax error</td>
                  <td class="empty">&nbsp;</td>
               </tr>
               <tr>
                  <td><strong><code><?=JSON_ERROR_UTF8; ?></code></strong></td>
                  <td><strong><code>JSON_ERROR_UTF8</code></strong></td>
                  <td>Malformed UTF-8 characters, possibly incorrectly encoded</td>
                  <td>PHP 5.3.3</td>
               </tr>
               <tr>
                  <td><strong><code><?=JSON_ERROR_RECURSION; ?></code></strong></td>
                  <td><strong><code>JSON_ERROR_RECURSION</code></strong></td>
                  <td>One or more recursive references in the value to be encoded</td>
                  <td>PHP 5.5.0</td>
               </tr>
               <tr>
                  <td><strong><code><?=JSON_ERROR_INF_OR_NAN; ?></code></strong></td>
                  <td><strong><code>JSON_ERROR_INF_OR_NAN</code></strong></td>
                  <td>
                     One or more
                     <a class="link" href="http://php.net/manual/de/language.types.float.php#language.types.float.nan" target="_blank"><strong><code>NAN</code></strong></a>
                     or <a class="link" href="http://php.net/manual/de/function.is-infinite.php" target="_blank"><strong><code>INF</code></strong></a>
                     values in the value to be encoded
                  </td>
                  <td>PHP 5.5.0</td>
               </tr>
               <tr>
                  <td><strong><code><?=JSON_ERROR_UNSUPPORTED_TYPE; ?></code></strong></td>
                  <td><strong><code>JSON_ERROR_UNSUPPORTED_TYPE</code></strong></td>
                  <td>A value of a type that cannot be encoded was given</td>
                  <td>PHP 5.5.0</td>
               </tr>
               <tr>
                  <td><strong><code><?=JSON_ERROR_INVALID_PROPERTY_NAME; ?></code></strong></td>
                  <td><strong><code>JSON_ERROR_INVALID_PROPERTY_NAME</code></strong></td>
                  <td>A property name that cannot be encoded was given</td>
                  <td>PHP 7.0.0</td>
               </tr>
               <tr>
                  <td><strong><code><?=JSON_ERROR_UTF16; ?></code></strong></td>
                  <td><strong><code>JSON_ERROR_UTF16</code></strong></td>
                  <td>Malformed UTF-16 characters, possibly incorrectly encoded</td>
                  <td>PHP 7.0.0</td>
               </tr>
            </tbody>
         </table>
      </div>
   </body>
</html>

form ที่อยู่ไทย

จากเรื่องที่แล้ว ฐานข้อมูลระดับจังหวัด อำเภอ ตำบล และ ใส่ฟิลเตอร์ให้ select box มาต่อด้วยการสร้างฟอร์ม ไปใช้ส่งข้อมูล

เป็นธรรมดาที่การกรอกข้อมูลส่วนตัว ประวัติต่างๆ อาจจะต้องการหลายที่อยู่ เช่น ที่อยู่ตามทะเบียนบ้าน ที่อยู่ปัจจุบัน ที่ทำงาน ถ้าต้องมากำหนดตัวกรองให้ select box ที่ละตัวก็เสียเวลา แก้ได้โดยเปลี่ยนไปใช้ class แทน id และใช้ attribute data- แทนแต่ละกลุ่ม และใช้ ajax ดึงข้อมูลมาทำ chained เวลาที่เปลี่ยนตัเลือกในตัวแม่ ให้ใช้เอแจ็กซ์ดึงข้อมูลที่เกี่ยวข้องมาแสดง update แทนตัวเลือก option ตัวลูกที่มีอยู่เดิม

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>AJAX jQuery.chosen By Pitt Phunsanit</title>
<link href="chosen/chosen.min.css" rel="stylesheet" type="text/css">
</head>
<body>
<fieldset>
	<legend>บ้าน</legend>
	<select name="home_province" class="areasProvince chosen-select" data-area="home" style="width:200px;">
		<option value="64">กระบี่</option>
		<option value="1">กรุงเทพมหานคร</option>
		<option value="56">กาญจนบุรี</option>
		<option value="34">กาฬสินธุ์</option>
		<option value="49">กำแพงเพชร</option>
		<option value="28">ขอนแก่น</option>
		<option value="13">จันทบุรี</option>
		<option value="15">ฉะเชิงเทรา</option>
		<option value="11">ชลบุรี</option>
		<option value="9">ชัยนาท</option>
		<option value="25">ชัยภูมิ</option>
		<option value="69">ชุมพร</option>
		<option value="45">เชียงราย</option>
		<option value="38">เชียงใหม่</option>
		<option value="72">ตรัง</option>
		<option value="14">ตราด</option>
		<option value="50">ตาก</option>
		<option value="17">นครนายก</option>
		<option value="58">นครปฐม</option>
		<option value="36">นครพนม</option>
		<option value="19">นครราชสีมา</option>
		<option value="63">นครศรีธรรมราช</option>
		<option value="47">นครสวรรค์</option>
		<option value="3">นนทบุรี</option>
		<option value="76">นราธิวาส</option>
		<option value="43">น่าน</option>
		<option value="77">บึงกาฬ</option>
		<option value="20">บุรีรัมย์</option>
		<option value="4">ปทุมธานี</option>
		<option value="62">ประจวบคีรีขันธ์</option>
		<option value="16">ปราจีนบุรี</option>
		<option value="74">ปัตตานี</option>
		<option value="5">พระนครศรีอยุธยา</option>
		<option value="44">พะเยา</option>
		<option value="65">พังงา</option>
		<option value="73">พัทลุง</option>
		<option value="53">พิจิตร</option>
		<option value="52">พิษณุโลก</option>
		<option value="61">เพชรบุรี</option>
		<option value="54">เพชรบูรณ์</option>
		<option value="42">แพร่</option>
		<option value="66">ภูเก็ต</option>
		<option value="32">มหาสารคาม</option>
		<option value="37">มุกดาหาร</option>
		<option value="46">แม่ฮ่องสอน</option>
		<option value="24">ยโสธร</option>
		<option value="75">ยะลา</option>
		<option value="33">ร้อยเอ็ด</option>
		<option value="68">ระนอง</option>
		<option value="12">ระยอง</option>
		<option value="55">ราชบุรี</option>
		<option value="7">ลพบุรี</option>
		<option value="40">ลำปาง</option>
		<option value="39">ลำพูน</option>
		<option value="30">เลย</option>
		<option value="22">ศรีสะเกษ</option>
		<option value="35">สกลนคร</option>
		<option value="70">สงขลา</option>
		<option value="71">สตูล</option>
		<option value="2">สมุทรปราการ</option>
		<option value="60">สมุทรสงคราม</option>
		<option value="59">สมุทรสาคร</option>
		<option value="18">สระแก้ว</option>
		<option value="10">สระบุรี</option>
		<option value="8">สิงห์บุรี</option>
		<option value="51">สุโขทัย</option>
		<option value="57">สุพรรณบุรี</option>
		<option value="67">สุราษฎร์ธานี</option>
		<option value="21">สุรินทร์</option>
		<option value="31">หนองคาย</option>
		<option value="27">หนองบัวลำภู</option>
		<option value="6">อ่างทอง</option>
		<option value="26">อำนาจเจริญ</option>
		<option value="29">อุดรธานี</option>
		<option value="41">อุตรดิตถ์</option>
		<option value="48">อุทัยธานี</option>
		<option value="23">อุบลราชธานี</option>
	</select>
	<select name="home_city" class="areasCity chosen-select" data-area="home" style="width:200px;">
	</select>
</fieldset>
<fieldset>
	<legend>ที่ทำงาน</legend>
	<select name="office_province" class="areasProvince chosen-select" data-area="office" style="width:200px;">
		<option value="64">กระบี่</option>
		<option value="1">กรุงเทพมหานคร</option>
		<option value="56">กาญจนบุรี</option>
		<option value="34">กาฬสินธุ์</option>
		<option value="49">กำแพงเพชร</option>
		<option value="28">ขอนแก่น</option>
		<option value="13">จันทบุรี</option>
		<option value="15">ฉะเชิงเทรา</option>
		<option value="11">ชลบุรี</option>
		<option value="9">ชัยนาท</option>
		<option value="25">ชัยภูมิ</option>
		<option value="69">ชุมพร</option>
		<option value="45">เชียงราย</option>
		<option value="38">เชียงใหม่</option>
		<option value="72">ตรัง</option>
		<option value="14">ตราด</option>
		<option value="50">ตาก</option>
		<option value="17">นครนายก</option>
		<option value="58">นครปฐม</option>
		<option value="36">นครพนม</option>
		<option value="19">นครราชสีมา</option>
		<option value="63">นครศรีธรรมราช</option>
		<option value="47">นครสวรรค์</option>
		<option value="3">นนทบุรี</option>
		<option value="76">นราธิวาส</option>
		<option value="43">น่าน</option>
		<option value="77">บึงกาฬ</option>
		<option value="20">บุรีรัมย์</option>
		<option value="4">ปทุมธานี</option>
		<option value="62">ประจวบคีรีขันธ์</option>
		<option value="16">ปราจีนบุรี</option>
		<option value="74">ปัตตานี</option>
		<option value="5">พระนครศรีอยุธยา</option>
		<option value="44">พะเยา</option>
		<option value="65">พังงา</option>
		<option value="73">พัทลุง</option>
		<option value="53">พิจิตร</option>
		<option value="52">พิษณุโลก</option>
		<option value="61">เพชรบุรี</option>
		<option value="54">เพชรบูรณ์</option>
		<option value="42">แพร่</option>
		<option value="66">ภูเก็ต</option>
		<option value="32">มหาสารคาม</option>
		<option value="37">มุกดาหาร</option>
		<option value="46">แม่ฮ่องสอน</option>
		<option value="24">ยโสธร</option>
		<option value="75">ยะลา</option>
		<option value="33">ร้อยเอ็ด</option>
		<option value="68">ระนอง</option>
		<option value="12">ระยอง</option>
		<option value="55">ราชบุรี</option>
		<option value="7">ลพบุรี</option>
		<option value="40">ลำปาง</option>
		<option value="39">ลำพูน</option>
		<option value="30">เลย</option>
		<option value="22">ศรีสะเกษ</option>
		<option value="35">สกลนคร</option>
		<option value="70">สงขลา</option>
		<option value="71">สตูล</option>
		<option value="2">สมุทรปราการ</option>
		<option value="60">สมุทรสงคราม</option>
		<option value="59">สมุทรสาคร</option>
		<option value="18">สระแก้ว</option>
		<option value="10">สระบุรี</option>
		<option value="8">สิงห์บุรี</option>
		<option value="51">สุโขทัย</option>
		<option value="57">สุพรรณบุรี</option>
		<option value="67">สุราษฎร์ธานี</option>
		<option value="21">สุรินทร์</option>
		<option value="31">หนองคาย</option>
		<option value="27">หนองบัวลำภู</option>
		<option value="6">อ่างทอง</option>
		<option value="26">อำนาจเจริญ</option>
		<option value="29">อุดรธานี</option>
		<option value="41">อุตรดิตถ์</option>
		<option value="48">อุทัยธานี</option>
		<option value="23">อุบลราชธานี</option>
	</select>
	<select name="office_city" class="areasCity chosen-select" data-area="office" style="width:200px;">
	</select>
</fieldset>
<script src="../jQuery/jquery-2.1.3.min.js"></script>
<script src="chosen/chosen.jquery.min.js"></script>
<script>
$(function(){

	/* select box filter */
	$('.chosen-select').chosen();
	$('.areasProvince').chosen().change(function(){
		child = $('.areasCity[data-area="'+$(this).data('area')+'"]');
		html = Array('<option value="">--Select--</option>');
		parent_id = $(this).val();
		if(parent_id == ''){
			child.html(html)
		}
		else
		{
			$.ajax({
				"cache" : true,
				"dataType" : "json",
				"success" : function(datas){
					$.each(datas, function(key, item){
						html.push('<option value="'+item.value+'">'+item.text+'</option>');
					});
					child.html(html);
					child.trigger('chosen:updated');
				},
				"url" : "thailandJSON.php?subdivisions=amphur&parent_id="+parent_id
			});
		}
	});

})
</script>
</body>
</html>

ฐานข้อมูลระดับจังหวัด อำเภอ ตำบล

จะทำระบบดึงข้อมูลหลายระดับ จังหวัด อำเภอ ตำบล ของประเทศไทย โดยถ้าเลือกจังหวัดแล้วจะแสดงอำเภอในจังหวัดนั่นๆ ให้เลือก แล้วสามารถเลือกตำบลได้ต่อไป

ข้อมูลตำบลมีให้โหลดโดยค้นหาว่า ฐานข้อมูล ไทยตำบล ผมไม่ทราบว่าใครเป็นคนทำคนแรก แต่ก็ขอขอบคุณที่แบ่งปันครับ ^_^
เวอร์ชั่นล่าสุดน่าจะเป็นของบล๊อก ScriptCase มีการใส่รหัสไปรษณีย์ เพิ่มเข้ามาด้วย

  1. สร้างตารางข้อมูลก่อน จากไฟล์ที่ดาวน์โหลดมาจากเว็บด้านบน หรือจากที่อื่นครับ
  2. สร้างไฟล์ thailandJSON.php ดึงข้อมูลตามตัวอย่าง
    <?php
    $dsn = mysqli_connect('localhost','root','','iso 3166-2') or die('Error ' . mysqli_error($link));
    mysqli_query($dsn, "SET NAMES 'utf8';");
    
    switch($_REQUEST['subdivisions'])
    {
    	case 'province' :
    	{
    		$sql = 'SELECT `PROVINCE_ID` AS value, `PROVINCE_NAME` AS text
    FROM `province`
    ORDER BY CONVERT(`PROVINCE_NAME` USING TIS620) ASC';
    	}break;
    	case 'amphur' :
    	{
    		$sql = 'SELECT `AMPHUR_ID` AS value, `AMPHUR_NAME` AS text
    FROM `amphur`
    WHERE `PROVINCE_ID` = '.$_REQUEST['parent_id'].'
    ORDER BY CONVERT(`AMPHUR_NAME` USING TIS620) ASC';
    	}break;
    	case 'district' :
    	{
    		$sql = 'SELECT `DISTRICT_ID` AS value, `DISTRICT_NAME` AS text
    FROM `district`
    WHERE `AMPHUR_ID` = '.$_REQUEST['parent_id'].'
    ORDER BY CONVERT(`DISTRICT_NAME` USING TIS620) ASC';
    	}break;
    }
    
    $result = mysqli_query($dsn, $sql);
    $datas = array();
    while($row = mysqli_fetch_array($result))
    {
    	array_push($datas, ['value' => $row['value'], 'text' => $row['text']]);
    }
    
    header('Content-type: application/json; charset=utf-8');
    echo json_encode($datas);
    
  3. ทดลองเรียกใช้งาน ดู
    • ../thailandJSON.php?subdivisions=province
    • ../thailandJSON.php?subdivisions=amphur&parent_id=1
    • ../thailandJSON.php?subdivisions=district&parent_id=22

อย่าลืมแก้ ข้อมูล การ connect ให้ตรงกับตอนสร้างฐานข้อมูลด้วย

json ค่าเป็น null

เขียน app ใน IPhone อยู่ๆก็ล่มตลอด ดูใน log สาเหตุคือ มีการพยามเอาค่า null เข้าไปใน property ที่รับค่า string ทั้งๆที่ object ตัวนั้นไม่น่าจะมีค่า null ได้ จะให้ตรวจค่าว่าเป็น NSNull Null แล้วไม่ต้องใช้มัน? เหมือนที่ใน forum ios บอกก็ไม่ไหวทำข้อมูลหายไปเฉยๆเลย

ลองตรวจข้อมูลที่มาจาก PHP ดูใน array print_r มาดูแล้วรายการนั้นก็เป็น string ปกติ แล้ว null มาจากไหน? เปิดไฟล์ json ดูอีกที ค่าเป็น null จริงๆ แปลว่า json_encode แปลงให้ผิด งงกว่าเดิมอีก มันไม่เคยเพี้ยนเลยนี่น่า ลองใช้ trim , htmlspecialchars ดูก็ไม่ช่วยอะไร

หาดูใน stackoverflow.com เจอเต็มๆ http://stackoverflow.com/questions/1972006/json-encode-is-returning-null เกิดจาก query จาก database แล้วส่งชุดตัวอักษรกลับมาอีกแบบ แก้ได้โดยใช้คิวรี่ SET CHARACTER SET utf8; ก่อน ลองเพิ่มคิวรี่ดู เรียบร้อยทำงานได้ตามปกติ ^_^ ซะที

ใช้ server คนละตัวนี้ต้องตรวจอะไรเยอะเหมือนกัน