Google Code Prettify

2016年5月11日 星期三

jQuery: 處理 json 資料

十年前,傳資料偏好用 xml 格式,因為 xml 太複雜,不好閱讀,也增加傳輸的資料量,現在大部份會選擇用 JSON 格式。這裡整理一個簡單的 jQuery 處理 JSON 資料的方法。

先看一下結果:
  • 啟動: 畫面上只有兩個按鍵,在按了按鍵後會顯現出資料。
  • click【html】:顯現出 bumblerMa.html 的內容
  • click【json】: 抓取 president.json 的內容,組成 html 後顯示出來。
上面的執行結果是要顯示兩個檔案的內容 bumblerMa.html 及 president.json,先來看一下這兩個檔案的內容:
  • bumblerMa.html: 這個 html 檔並非完整包含 <html>、<body> 等 tag 的 html,因為這個檔的內容在底下的程式中,是被載入後鑲嵌於既有網頁中。
<H1>Ma the bumbler</H1>

<H3>A former heart-throb loses his shine</H3>
<DIV>Nov 17th 2012 | TAIPEI | From the print edition</DIV>
<br/>
WHEN he was first elected in 2008, Taiwan’s president, Ma Ying-jeou, offered Taiwanese high hopes that the island’s economy would open a new chapter. He promised ground-breaking agreements with China to help end Taiwan’s growing economic marginalisation. At the time, Mr Ma’s image was of a clean technocrat able to rise above the cronyism and infighting of his party, the Kuomintang (KMT). He was a welcome contrast to his fiery and pro-independence predecessor, Chen Shui-bian, now in jail for corruption.
<br/>
Five years on, and despite being handily re-elected ten months ago, much has changed. In particular, popular satisfaction with Mr Ma has plummeted, to a record low of 13%, according to the TVBS Poll Centre. The country appears to agree on one thing: Mr Ma is an ineffectual bumbler.
<br/>
  • president.json: 要特別注意,JSON 的內容要完全符合 JSON 格式規定,如果不符合,一般來說不會有錯誤訊息,只是程式就停止執行,不會有任何反應,debug 比較廢時。
[
    {
        "title":"Ma the bumbler",
        "sub-title":"A former heart-throb loses his shine",
        "date":"Nov 17th 2012 | TAIPEI | From the print edition",
        "content":"WHEN he was first elected in 2008, Taiwan’s president, Ma Ying-jeou, offered Taiwanese high hopes that the island’s economy would open a new chapter. He promised ground-breaking agreements with China to help end Taiwan’s growing economic marginalisation. At the time, Mr Ma’s image was of a clean technocrat able to rise above the cronyism and infighting of his party, the Kuomintang (KMT). He was a welcome contrast to his fiery and pro-independence predecessor, Chen Shui-bian, now in jail for corruption.<br/>Five years on, and despite being handily re-elected ten months ago, much has changed. In particular, popular satisfaction with Mr Ma has plummeted, to a record low of 13%, according to the TVBS Poll Centre. The country appears to agree on one thing: Mr Ma is an ineffectual bumbler."
    },
    {
        "title":"A Tsai is just a Tsai",
        "sub-title":"The election of an independence-leaning president would put Taiwan back in the international spotlight",
        "date":"Jan 9th 2016 | TAIPEI | From the print edition",
        "content":"UNDETERRED by the rain, the crowd leaps to its feet shouting “We’re going to win” in Taiwanese as their presidential candidate, Tsai Ing-wen, begins her stump speech. Some rattle piggy banks to show that their party, the Democratic Progressive Party (DPP), relies on, and serves, the little guy—as opposed to the ruling Kuomintang (KMT), backed by businesses and fat cats and one of the world’s richest political institutions. Taiwan’s voters go to the polls on January 16th in what is likely to prove a momentous election both for the domestic politics on the island and for its relations with the Communist government in China that claims sovereignty over it. Eight years of uneasy truce across the Taiwan Strait are coming to an end."
    }
]
現在進入主題,看一下程式怎麼寫 … 只有一個網頁,如下:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<script type="text/javascript" src="<%=request.getContextPath()%>/js/jquery-1.12.3.min.js"></script>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>AJAX</title>

<style type="text/css">
    .button {
        background-color: #7CA05F;
        border: none;
        color: white;
        padding: 15px 32px;
        text-align: center;
        text-decoration: none;
        display: inline-block;
        font-size: 16px;
        margin: 4px 2px;
        cursor: pointer;
    }
</style>
    
<script type="text/javascript">
$(function() {
    $('#html-button .button').bind('click', (function() {
        $("#article").load('../html/bumblerMa.html');
    }));

    $('#json-button .button').bind('click', (function() {
        $.getJSON('../json/president.json', function(data) {
            $('#article').empty();
            $.each(data, function(entryIdx, entry) {
                var html = '';
                html += '<H1>' + entry['title'] + '</H1>';
                html += '<H3>' + entry['sub-title'] + '</H3>';
                html += '<DIV>' + entry['date'] + '</DIV>';
                html += '<br/>' + entry['content'] + '</br>';
                $('#article').append(html);
            }); 
        });
    }));
});
</script>

</head>
<body>

<div id="article"></div>
<div id="html-button">
    <input type="button" class="button" value="html"/>
</div>

<div id="json-button">
    <input type="button" class="button" value="json"/>
</div>


</body>
</html>
  • 綠色部份: 引入 jQuery 函式庫。
  • 藍色部份: 這是要顯示資料的地方,一開始沒有任何內容!
  • 黃色部份: 這表示要找到一個在 id="html-button" 的 tag 下,任何一個含有 class="button" 的 tag,及一個在 id="json-button" 的 tag 下,任何一個含有 class="button" 的 tag。
  • 紅色部份: 在網頁載入時,為這兩個按鍵 bind click 事件,這樣當這兩個按鍵被 click 時,就會執行 bind 時所指定的 function。
  • 紫色部份: 使用 load 將 載入的內容顯示在 tag 上,這就可以了解,為什麼這個 bumblerMa.html 不是完整的 html 檔了。
  • 橘色部份: 使用 getJSON 載入 json 檔,載入的資料會放在 data 那個變數裡,再透過 $.each 將 JSON 的內容讀出來,$.each 一次取出一個元素,以 president.json 的內容來說,共有兩個元素,每個元素有 title、sub-title、date、content 四項資料。
上面非同步抓取 json 資料的程式也可以改用如下的方法:
$('#json-button2 .button').bind('click', (function() {
    $.ajax({
        type: 'get',
        url: '../json/president.json',
        dataType: 'json',
        success: function(data, result) {
         $.each(data, function(entryIdx, entry) {
                $('#article').empty();
                var html = '';
                html += '<H1>' + entry['title'] + '</H1>';
                html += '<H3>' + entry['sub-title'] + '</H3>';
                html += '<DIV>' + entry['date'] + '</DIV>';
                html += '<br/>' + entry['content'] + '</br>';
                $('#article').append(html);
           }); 
        }
    });
}));

2016年5月2日 星期一

spring MVC + jQuery ajax

現在的 web 系統前端程式的比重越來越重,以 ajax 方式與後端溝通成為常態,這篇要整理的是,當後端為 spring MVC 架構,前端使用 jQuery ajax 的情況下,兩者如何互動? 這裡的程式放在 Bitbucket,如有需要請自行下載。程式很簡單,如下,在輸入股票代號後,顯示出股票名稱及價格。
  1. 輸入前
  2. 輸入後 (不需要 submit)
  • jar 檔
因為這個程式在 MVC 傳回給前端時,是採用 JSON 格式,所以,請在 pom.xml 中增加如下設定。
<dependency>  
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>${jackson.json.version}</version>
</dependency>
          
<dependency>  
    <groupId>com.fasterxml.jackson.dataformat</groupId>  
    <artifactId>jackson-dataformat-xml</artifactId>  
    <version>${jackson.json.version}</version>  
</dependency> 
  • 資料來源
因為這裡要讓程式儘量簡單,不從資料庫取得股票相關資料,而是設定在 spring 的 bean configure file 裡,如下,只有兩檔股票。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="stock2330" class="idv.steven.mvc.form.StockForm"
        p:code="2330"
        p:name="台積電"
        p:price="153.0" 
    />
        
    <bean id="stock2490" class="idv.steven.mvc.form.StockForm"
        p:code="2490"
        p:name="友達"
        p:price="9.4"
    />

    <bean id="allStocks" class="idv.steven.mvc.form.Stocks">
        <property name="stocks">
            <map>
                <entry key="2330">
                    <ref bean="stock2330"/>
                </entry>
                
                <entry key="2490">
                    <ref bean="stock2490"/>
                </entry>                
            </map>
        </property>
    </bean>
</beans>

上面設定檔中有兩個類別 StockForm 及 Stocks,分別如下:
public class StockForm {
    private String code;
    private String name;
    private Double price;
    
    public StockForm() {}
    
    public StockForm(String code, String name, Double price) {
        this.code = code;
        this.name = name;
        this.price= price;
    }
    
    public String getCode() {
        return code;
    }
    public void setCode(String code) {
        this.code = code;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Double getPrice() {
        return price;
    }
    public void setPrice(Double price) {
        this.price = price;
    }
}
public class Stocks {
    private Map<String, StockForm> stocks;

    public Map<String, StockForm> getStocks() {
        return stocks;
    }

    public void setStocks(Map<String, StockForm> stocks) {
        this.stocks = stocks;
    }
}
  • ajax 網頁
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<script type="text/javascript" src="<%=request.getContextPath()%>/js/jquery-1.12.3.min.js"></script>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>查詢股價</title>

<script type="text/javascript">
var message;

$(function() {
    $("#code").bind("keyup", function(e) {
        if ($("#code").val().length != 4) {
            return;
        }

        var myUrl = "<%=request.getContextPath()%>/searchStock.do?code=" + $('#code').val();
        
        $.ajax({
            type: 'get',
            url: myUrl,
            dataType: 'json',
            success: function(data, result) {
                if (data.responseMsg.status == "success") {
                    message = $.parseJSON(data.jsondata);
                    $('#name').html('股票名稱: ' + message.name);
                    $('#price').html('股票價格: ' + message.price);
                }
                else {
                    alert('fail');
                    alert(data.responseMsg.message);
                }
            },
            error: function() {
                alert('error');
            }
        });
    });
});
</script>

</head>
<body>

<form>
    股票代號: <input id="code" name="code" type="text"/><br/>
    <div id="name"></div>
    <div id="price"></div>
</form>

</body>
</html> 
  1. bind 事件 (綠色部份): 網頁只有一個可輸入股票代號的欄位,在網頁載入時,透過 jQuery 先 bind 的一個 keyup 的事件給這個輸入欄位,當在這個 input tag 輸入資料時,都會觸發事件。
  2. 檢查條件是否符合需要 (橘色部份): 我們假設股票代號都是四碼 (實務上雖以四碼為主,也有許多不是四碼的股票!),所以當輸入的代號不到四碼之前都直接返回。
  3. 網址與參數 (紫色部份): 傳給後端的參數只有 code,即股票代號,因為採用 get 的方式,直接接在網址上即可。
  4. JSON (黃色部份): 指定傳回的是 json 格式,因為是 json,利用 jQuery 提供的 API (parseJSON) 解析,至於解析後為什麼會有 name、price 兩個屬性? 這是對應上面 StockForm 類別,因為在 controller 中傳回的就是 StockForm 類別轉化成的 JSON 資料。
  • 傳回類別
public class ResponseMessage {
    private String message;
    private String status;
    
    public ResponseMessage(String message, String status) {
        this.message = message;
        this.status = status;
    }
    
    public String getMessage() {
        return message;
    }
    public void setMessage(String message) {
        this.message = message;
    }
    public String getStatus() {
        return status;
    }
    public void setStatus(String status) {
        this.status = status;
    }
}
定義這個類別,用來傳回成功或失敗的訊息,及查詢所得結果,這個類別要有那些欄位當然依程式的需要來設計。
  • Controller
@Controller
public class StockController {
    @Autowired
    private Stocks allStocks;
    
    @RequestMapping(value="/searchStock", method=RequestMethod.GET)
    public @ResponseBody Map<String, Object> getStockPrice(String code) {
        Map<String, Object> result = new HashMap<String, Object>();
        
        Map<String, StockForm> stocks = allStocks.getStocks();
        StockForm stock = stocks.get(code);
        
        if (stock != null) {
            result.put("jsondata", toJson(stock));
            result.put("responseMsg", new ResponseMessage("取得股票資料", "success"));
        }
        
        return result;
    }
    
    public String toJson(Object object) {
        try {
            ObjectMapper mapper = new ObjectMapper();
            return mapper.writeValueAsString(object);
        }
        catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }
}
  1. 載入資料 (藍色部份): 前面在 beans-config.xml 中有初始化 Stocks 類別的值,在這裡透過 @Autowired 載入。
  2. 網址 (綠色部份): 在上一篇就有提到網址要寫在 @RequestMapping 裡,當只有網址一個屬性時,可以寫成 @RequestMapping("/searchStock"),但是因為在這裡要限定只接收 GET 傳送過來的資料,變成有兩個參數,所以網址要寫成 value="searchStock"。
  3. 傳回 JSON (橘色部份): 當返回的值不是 html 頁面時 (JSON、xml ...),需要加上這個 @ResponseBody。
  4. 接收參數 (紅色部份): controller 一般會定義一個 form class 來接收 jsp 傳過來的資料,但是因為這個程式太簡單了,只會收到一個參數,所以直接用 code 這個參數去接收就可以了。
  5. 傳回值 (紫色部份): 傳回值不一定要用 map,不過這是一個相當通行且簡單的方法,這裡的 map 會放入兩個值 - jsondata 及 responseMsg,responseMsg 只是指出查詢是否成功,真正的傳回值在 jsondata,這部份要轉成 JSON 格式,文章最開頭在 pom.xml 中引入的 jar 檔,就是 JSON 處理的類別庫。

  • see also

        jQuery: 處理 json 資料





【日影 - I am a hero (喪屍末日戰)】
這部電影算是恐怖片,也算英雄片? 但是一想到主角是大泉洋,很難把他和英雄聯想在一起!但不管怎麼樣,他在劇中的名字是「鈴木英雄」,是真正的英雄! 我看過的日劇、日影很多,大泉洋主演的這是第一次! 在電影宣傳裡有村架純飾演的早狩比呂美相較於長澤雅美飾演的更受重視,看之前我還以為是有村架純主演,實際上劇中長澤雅美的戲份不會比較少,也比較有表現的空間,只不過早狩比呂美和鈴木英雄的特殊關係,會讓有村架純就算不出現在螢幕上,也會隱隱感覺到她的存在。
這部電影後來又拍了前傳 - 「請叫我英雄 最初之日」,以長澤雅美飾演的藪為主角,講述疫情爆發第一天的情況,這五集的短片拍攝的手法比較特別,是以類紀錄片的方式拍攝,裡面很多時候還會是單鏡頭拍攝,為什麼要這麼拍呢? 這個嘛~ 得請巷子裡的人出來講講才知道了 ...
長澤雅美人長得漂亮演技又好,看她演戲還蠻享受的。