參考PHP官方手冊
呼叫$db->lastInsertId();
Use SQLite to record user history
如果我們想記錄使用者在APP上互動的歷史,我們可能會需要紀錄她在某個時候,做了什麼樣的一個動作。在Android中有很多可以進行紀錄的方法,其中SQLite API最被廣為人知的應用就是用來實作TodoList、通訊錄或是其他牽涉到Client端資料庫的應用,而這邊我們也使用SQLite來進行紀錄使用者紀錄。
SQLite的教學可以參考Vogella,寫得相當完整且英文描述也非常的平易近人
教學文中主要將使用SQLite存取資料的功能分成三個物件
包括了:
以下分別介紹這三者的起手式。
註1:時間的格式有標準的規定,例如年份並不支援大寫Y。
SQLite也很有個性,有它自己能接受的時間格式。(繁體中文版本)
藉由以上三個物件的實作,已經可以應付最基礎的資料存取功能。
儲存完使用者的歷史紀錄之後,接著就要開始著手偏好分析。
JAVA的日期時間取得 @ ROACH部落落 :: 痞客邦 PIXNET ::
SQLite的教學可以參考Vogella,寫得相當完整且英文描述也非常的平易近人
教學文中主要將使用SQLite存取資料的功能分成三個物件
包括了:
- 自訂的SQLiteOpenHelper(用來執行資料庫的一些任務)
- Getter & Setter(存取資料)
- 還有資料存取物件(Data Access Object,DAO,為連結以上兩者的橋樑)
以下分別介紹這三者的起手式。
Custom SQLiteOpenHelper
public class DBHelper extends SQLiteOpenHelper{
public DBHelper(Context context) {
// DATABASE.NAME(String)與DATABASE.VERSION(int)都是我自訂interface的數值
// 分別代表資料庫的名稱與版本
super(context, DATABASE.NAME, null, DATABASE.VERSION);
}
// 資料庫一開始建置時會執行的動作將在onCreate生命週期中觸發
@Override
public void onCreate(SQLiteDatabase db) {
// 這邊和SQL很像,就用你熟悉的方式輸入指令
String sql = "CREATE TABLE IF NOT EXISTS " + DATABASE.TABLE +
" (" +
" " + DATABASE.UID + " TEXT," +
" " + DATABASE.DID + " TEXT," +
" " + DATABASE.TIME + " DATETIME," +
" " + DATABASE.RID + " TEXT" +
");";
// 執行
db.execSQL(sql);
}
// 若發現更新後的資料庫(版本不一樣時),刪除原本的資料庫,並建置一個新的
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
String sql = "DROP TABLE IF EXISTS " + DATABASE.TABLE;
db.execSQL(sql);
onCreate(db);
}
}
Getters & Setters
public class UserHistory {
private String uid;
private String did;
private String time;
private String rid;
public String getUid() {
return uid;
}
public void setUid(String uid) {
this.uid = uid;
}
public String getDid() {
return did;
}
public void setDid(String did) {
this.did = did;
}
public String getTime() {
return time;
}
... // 以此類推
Data Access Object
// 這邊是最重要的部分,所有有關資料存取的動作我們都在這裡調用
// 如此一來就不需要動到以上兩個物件,妥善隱藏資料的本質
public class HistoryDAO {
private SQLiteDatabase database;
private DBHelper dbHelper;
public HistoryDAO(Context context) {
dbHelper = new DBHelper(context);
}
// 開啟資料庫
public void open() throws SQLException {
database = dbHelper.getWritableDatabase();
}
// 關閉資料庫
public void close() {
dbHelper.close();
}
// 輸入資料到資料庫中
public void insert(String uid, String did, String rid) {
// 使用key/value的方式儲存資料,再丟給資料庫insert
ContentValues values = new ContentValues();
values.put(DATABASE.UID, uid);
values.put(DATABASE.DID, did);
values.put(DATABASE.TIME, getCurrentTime());
values.put(DATABASE.RID, rid);
database.insert(DATABASE.TABLE, null, values);
}
// 因為我們在台灣,需要調整時區,以獲得使用者產生互動的正確時間
private String getCurrentTime() {
// 註1
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT+8"));
String currentTime = dateFormat.format(new java.util.Date());
return currentTime;
}
// 這邊因為我在測試是不是真的有輸入成功,於是在進行query後取得數量
public int size() {
Cursor cursor = database.query(DATABASE.TABLE, new String[] {
DATABASE.UID,
DATABASE.DID,
DATABASE.TIME,
DATABASE.RID }, null, null, null, null, null);
return cursor.getCount();
}
}
註1:時間的格式有標準的規定,例如年份並不支援大寫Y。
SQLite也很有個性,有它自己能接受的時間格式。(繁體中文版本)
藉由以上三個物件的實作,已經可以應付最基礎的資料存取功能。
儲存完使用者的歷史紀錄之後,接著就要開始著手偏好分析。
Reference,
JAVA的日期時間取得 @ ROACH部落落 :: 痞客邦 PIXNET ::
The Future of Web
由於我的研究領域牽涉到了本體論(Ontology)
也就是Tim Berners-Lee(2004)提出的語意網Layer Cake中的一個部分:

因此我略懂RDF也是很正常的。
就像身為一個汽車維修員,隨身攜帶工具也是很正常的一樣XD
所以以下此篇將以語意網的角度淺談Web的未來。
一開始,讓我們先來了解一下Web 1.0 2.0與3.0的不同:
語意網中有一層標準和現在的HTML標準很接近,但是又有點些許的不同。
這層便是RDF。(在Semantic Web Layer Cake中Ontology的下層)
和HTML同為XML檔案,且都是W3C定義的標準,其簡介如下:
RDF 語言是 W3C 的語意網活動的组成部分。W3C 的「語意網展望(Semantic Web Vision)」的目標是:
以下是RDF的範例代碼。在RDF中,所有的標籤皆可以是自訂的:
其實對於自定義標籤的限制是越來越寬了
從一開始的head、body,到後來還有header、footer等標籤
某種程度看來也是在幫助網頁本身的資訊能夠擁有更精確的涵義
就現在來說,我並不確定我們屬於Web的2.0、2.3、2.5的哪個階段
但可以確定的是,我們已經慢慢地朝著Web 3.0的方向前進
至於4.0是什麼樣的世界 真的很難以想像......
[1] Semantic Web for the Working Ontologist. D.Allemang & J.Hendler, 2008.
也就是Tim Berners-Lee(2004)提出的語意網Layer Cake中的一個部分:
因此我略懂RDF也是很正常的。
就像身為一個汽車維修員,隨身攜帶工具也是很正常的一樣XD
所以以下此篇將以語意網的角度淺談Web的未來。
一開始,讓我們先來了解一下Web 1.0 2.0與3.0的不同:
分類(Web)
描述
1.0
沒有互動的機制,所有的資訊都是單向的流通。
2.0
最主要的精神是可以進行協作與共享,模範生有Wikipedia,Facebook等等。
「Anyone can say Anything on Any topic」這個AAA Slogan[1]也將其精神表現得淋漓盡致。
3.0
「該詞包含多層含義,用來概括網際網路發展過程中可能出現的各種不同的方向和特徵,包括:將網際網路本身轉化為一個泛型資料庫;跨瀏覽器、超瀏覽器的內容投遞和請求機制;人工智慧技術的運用;語意網。」
※泛型資料庫:我認為是與Linked Data相關概念,請參考Vcard。
分類(Web)
|
描述
|
1.0
|
沒有互動的機制,所有的資訊都是單向的流通。
|
2.0
|
最主要的精神是可以進行協作與共享,模範生有Wikipedia,Facebook等等。
「Anyone can say Anything on Any topic」這個AAA Slogan[1]也將其精神表現得淋漓盡致。
|
| 3.0 |
「該詞包含多層含義,用來概括網際網路發展過程中可能出現的各種不同的方向和特徵,包括:將網際網路本身轉化為一個泛型資料庫;跨瀏覽器、超瀏覽器的內容投遞和請求機制;人工智慧技術的運用;語意網。」
※泛型資料庫:我認為是與Linked Data相關概念,請參考Vcard。
|
語意網中有一層標準和現在的HTML標準很接近,但是又有點些許的不同。
這層便是RDF。(在Semantic Web Layer Cake中Ontology的下層)
和HTML同為XML檔案,且都是W3C定義的標準,其簡介如下:
RDF 語言是 W3C 的語意網活動的组成部分。W3C 的「語意網展望(Semantic Web Vision)」的目標是:
- Web 資訊擁有精確的涵義
- Web 資訊可以被電腦理解並處理
- 電腦可以從 Web 上整合資訊
以下是RDF的範例代碼。在RDF中,所有的標籤皆可以是自訂的:
<?xml version="1.0"?>
<RDF>
<Description about="http://www.w3school.com.cn/RDF">
<author>David</author>
<homepage>http://www.w3school.com.cn</homepage>
</Description>
</RDF>
而從HTML4、HTML5甚至到HTML6來看其實對於自定義標籤的限制是越來越寬了
從一開始的head、body,到後來還有header、footer等標籤
某種程度看來也是在幫助網頁本身的資訊能夠擁有更精確的涵義
就現在來說,我並不確定我們屬於Web的2.0、2.3、2.5的哪個階段
但可以確定的是,我們已經慢慢地朝著Web 3.0的方向前進
至於4.0是什麼樣的世界 真的很難以想像......
References,
[1] Semantic Web for the Working Ontologist. D.Allemang & J.Hendler, 2008.
標籤:
Semantic Web
Resources of implement custom TabHostButton, ListView with images and texts, and ProgressBar when loading data

Mealionaier是一個可以學習你對食物的偏好
精準推薦給你附近美食的APP。
然而為了更好的使用者體驗,需要實現方便使用、直覺的UI
在實現某些UI時需要一些tricks。
在這邊和大家介紹我之前碰壁時,有如菩薩般出現拯救我的資源
等我論文告一段落了,再來分享詳細的程式碼教學:)
利用RadioGroup與Fragments創造Tab Buttons效果

Tutorial link - 开源项目AndroidUtil-采用Fragment实现TabHost | 方杰
- 每個Tab都是一個RadioGroup的項目。
- 為RadioGroup新增它的項目符號後,再將選取按鍵(checkbox)隱藏起來
- 當使用者點擊Tab時,用程式的方式選取對應的項目。
在ListView中妥善顯示圖片與文字

Tutorial link - Android Adapter Good Practices « Piwaï.info
- 做好忍痛放棄ArrayList、SimpleAdapter等內建Adapter的心理準備
- 繼承BaseAdapter,自訂一個新的Adapter from scratch
- 了解ViewHolder的patterns
動態載入ListView的items並在載入時顯示ProgressBar
Tutorial link - Endless Scrolling with AdapterViews | CodePath Android Cliffnotes
- 學會操作onScrollListener
- 繼承BaseAdapter,自訂一個新的Adapter
- 深入了解如何操作getViewType、getView等BaseAdapter中的method
Git rebase 實戰演練
自從使用Git之後,因為開始變得我天不怕地不怕
有時候上個廁所也好,出門覓個食也罷
毋忘隨時來個commit,想說養成save的好習慣
所以以下的事情是家常便飯XD
久而久之一個專案內不小心就充滿了凌亂的commits
Git會記錄我們coding的一舉一動
我們已經知道藉由使用branch可以方便的讓我們切換版本
但是魔鬼藏在細節裡
真正紀錄我們細節的,其實就是那些年,無時無刻盯著我們
吃飯、睡覺、打code的commits
當我們每次進行commit的時候,都會有相對應的SHA碼
(SHA是一種演算法,在這邊就當成它是經由複雜的運算,來達到讓每次進行commit的時候,都不會有重複的狀況而採取的一種紀錄方法)
就好像我們每個人都有自己的身分證字號一樣
你可以在這邊找到你的commit他們的身分證字號

很棒吧,他們都已經排排站了喔
紅色框框的地方是你點一下就可以作一個複製的動作。
那今天就是要來示範如何將多餘的commit進行一個整理的動作
讓本來凌亂的紀錄,連小明都看得懂。
首先呢
請移駕到你的command line,我用的是Github提供的那個工具
先cd到你專案的資料夾
你會發現最舊commit它保管了最多的pick
換句話說,越舊的commit提供你更大的調整範圍
你可以根據自己的需求來進行pick reowrd edit squash fixup exec等動作
這些動作我想以後再介紹,今天重點就在使用squash
pick和squash最主要的不同是
當你用rebase指令開啟了一個commit
你會看到很多個pick(如果它不是最新的commit)
而當你把其中幾個pick註解掉或是刪掉的話
沒錯,它們就會像變了心的女朋友一樣
頭也不回的走了,永遠都回不來了!
那如果你是用squash的話,就很像是吾皇擁有後宮三千佳麗
卻無奈於經濟不景氣,本來一張鈔票可以買一碗牛肉麵
現在只能買一碗滷肉飯......所以把三千砍到剩千五的道理一樣
squash是向前融合(meld into),所以它的前一個動作不可為squash
(拜託不要呀~千五已經很少了......)
以我自己的專案為例,我只挑了最重要的幾條沒有squash
剩下的都向前融合(這邊用#會讓Git跳過不讀,等於註解):
沒有意外的話等一會又會有記事本彈出來和你報告:
「稟告皇上,在下已經把舊的幾個宮女fire掉了
然後請了一個最新的代替那些位置,您要為她取一個新的名字嗎?」
其實你可以把他們全部砍掉,留一個名稱就好。
「朕,覺der可以叫做Basic Settings。」
「喳!」
中途你可能會遇到重複的現象,一直叫你取名幹嘛的
不過照著範例走,應該不會有問題。
Finally,我們就完成了整理commits的動作。
來ㄧ張selfie~

有時候上個廁所也好,出門覓個食也罷
毋忘隨時來個commit,想說養成save的好習慣
所以以下的事情是家常便飯XD
久而久之一個專案內不小心就充滿了凌亂的commits
Git會記錄我們coding的一舉一動
我們已經知道藉由使用branch可以方便的讓我們切換版本
但是魔鬼藏在細節裡
真正紀錄我們細節的,其實就是那些年,無時無刻盯著我們
吃飯、睡覺、打code的commits
當我們每次進行commit的時候,都會有相對應的SHA碼
(SHA是一種演算法,在這邊就當成它是經由複雜的運算,來達到讓每次進行commit的時候,都不會有重複的狀況而採取的一種紀錄方法)
就好像我們每個人都有自己的身分證字號一樣
你可以在這邊找到你的commit他們的身分證字號
Github(網頁版)

Github(桌面版)
很棒吧,他們都已經排排站了喔
紅色框框的地方是你點一下就可以作一個複製的動作。
那今天就是要來示範如何將多餘的commit進行一個整理的動作
讓本來凌亂的紀錄,連小明都看得懂。
首先呢
請移駕到你的command line,我用的是Github提供的那個工具
先cd到你專案的資料夾
git rebase -i [SHA/commit版本號碼]
我這邊直接開最舊的起來改,中途會彈出一個文字檔,你的看起來應該也和下面的差不多:
pick e466349 Loading files
pick 8af9b37 Create README.md
pick 161534d small revision
pick d992172 small revision
pick 2cf85cd easy connected
pick 4769884 Nov 20
pick 92b4484 Nov 21
pick 30d9919 Nov 21 noon
pick bc92c4a Nov evening
pick 4771349 Nov 22
pick 266f43b Nov 23
pick a1990a4 Nov 24
pick 2100e46 Nov 24 v2
pick 196b0dc Nov 25
...
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
在這邊你可以自己一個一個commit慢慢玩你會發現最舊commit它保管了最多的pick
換句話說,越舊的commit提供你更大的調整範圍
你可以根據自己的需求來進行pick reowrd edit squash fixup exec等動作
這些動作我想以後再介紹,今天重點就在使用squash
pick和squash最主要的不同是
當你用rebase指令開啟了一個commit
你會看到很多個pick(如果它不是最新的commit)
而當你把其中幾個pick註解掉或是刪掉的話
沒錯,它們就會像變了心的女朋友一樣
頭也不回的走了,永遠都回不來了!
那如果你是用squash的話,就很像是吾皇擁有後宮三千佳麗
卻無奈於經濟不景氣,本來一張鈔票可以買一碗牛肉麵
現在只能買一碗滷肉飯......所以把三千砍到剩千五的道理一樣
squash是向前融合(meld into),所以它的前一個動作不可為squash
(拜託不要呀~千五已經很少了......)
以我自己的專案為例,我只挑了最重要的幾條沒有squash
剩下的都向前融合(這邊用#會讓Git跳過不讀,等於註解):
#一開始的檔案
pick e466349 Loading files
squash 8af9b37 Create README.md
squash 161534d small revision
squash d992172 small revision
squash 2cf85cd easy connected
#建立主機伺服器連線
pick 4769884 Nov 20
squash 92b4484 Nov 21
squash 30d9919 Nov 21 noon
#動畫完成
pick bc92c4a Nov evening
squash 4771349 Nov 22
squash 266f43b Nov 23
squash a1990a4 Nov 24
squash 2100e46 Nov 24 v2
squash 196b0dc Nov 25
squash 2e6cbcd Nov 25 v2
squash bebe100 Nov 25 v3
...
接著請儲存後關掉記事本沒有意外的話等一會又會有記事本彈出來和你報告:
# This is a combination of 5 commits.
# The first commit's message is:
Loading files
# This is the 2nd commit message:
Create README.md
# This is the 3rd commit message:
small revision
# This is the 4th commit message:
small revision
shake new activity
# This is the 5th commit message:
easy connected
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# rebase in progress; onto 4f12ce8
# You are currently editing a commit while rebasing branch 'ManualEntry-v2' on '4f12ce8'.
#
意思就是「稟告皇上,在下已經把舊的幾個宮女fire掉了
然後請了一個最新的代替那些位置,您要為她取一個新的名字嗎?」
其實你可以把他們全部砍掉,留一個名稱就好。
「朕,覺der可以叫做Basic Settings。」
「喳!」
Basic settings
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# rebase in progress; onto 4f12ce8
# You are currently editing a commit while rebasing branch 'ManualEntry-v2' on '4f12ce8'.
#
一樣儲存後關掉記事本中途你可能會遇到重複的現象,一直叫你取名幹嘛的
不過照著範例走,應該不會有問題。
Finally,我們就完成了整理commits的動作。
來ㄧ張selfie~

References
Reflection on using Git
這篇將依照目前使用Git的心得,簡單的對Git與提供保存Git與原始碼在雲端的網路服務Github進行介紹。
Git是什麼? 為什麼要用Git?
Git是一種版本控制的系統。
換句話說,Git可以提供你整個專案版本的監控
藉由使用版本控制系統,你可以自由地在每個版本之間切換
你可以測試不同的版本並且備份你的版本。
Git的備份功能和傳統我們認知的備份有很大的不同。
依照傳統的作法,我們在進行一個專案時,若遇到不同的想法想測試,
我們可能會先將檔案(或整個專案)複製一份,並且用複製出來的新版本進行修改
原版就先進行保存。所以這時候你可能會遇到這種情況:
之後當我們的每個版本都有一定程度的發展,你可能會遇到這種情況:
這種情況我認為是各有優缺啦:
依照傳統的方式,若本來要發展的版本分岐不大,確實可以使用這種方法,因為若沒有進入後續維護的階段,其實非常的省時省力。但一旦踏入了維護的階段,看到那排山倒海而來的檔案......你可能會全身無力......
那使用Git呢? 由於Git是一個工具,我們在學習一樣工具如何使用的過程中多多少少會耗費一些學習的時間,但就我用到目前為止而言,覺得經濟效益確實比傳統的作法好上太多。使用Git需要使用命令工具列(也就是按壓鍵盤Windows+R後,輸入cmd會出現的東西)輸入一些命令,但有了Github提供的圖形化介面之後,其實已經好用很多。
使用Git的優缺點:
以前我用傳統的備份方式,有時候常常在不同版本的專案資料夾內把檔案拉來拉去,後來也忘記這些名字一樣的檔案到底哪個是有改過哪個是沒有改過的;或是說有時候不小心在系統詢問要不要取代時不小心按了取代,然後心血便付諸流水。
至少現在我用了Git,我能很肯定地說我可以很大膽的coding,我可以肆無忌憚的對程式碼大興土木,只要在每個關鍵點記得做一個記錄的動作,之後就算我改壞了,一行指令就回來啦。這時候你真的可以體會什麼叫作天無絕人之路、什麼叫作浪子回頭金不換XD
學習使用Git可以參考:
註2:Git的commit
延伸閱讀:我的Git常用指令(準備中)
Git是什麼? 為什麼要用Git?
Git是一種版本控制的系統。
換句話說,Git可以提供你整個專案版本的監控
藉由使用版本控制系統,你可以自由地在每個版本之間切換
你可以測試不同的版本並且備份你的版本。
Git的備份功能和傳統我們認知的備份有很大的不同。
依照傳統的作法,我們在進行一個專案時,若遇到不同的想法想測試,
我們可能會先將檔案(或整個專案)複製一份,並且用複製出來的新版本進行修改
原版就先進行保存。所以這時候你可能會遇到這種情況:
之後當我們的每個版本都有一定程度的發展,你可能會遇到這種情況:
這種情況我認為是各有優缺啦:
Pros
|
Cons
|
|
|
依照傳統的方式,若本來要發展的版本分岐不大,確實可以使用這種方法,因為若沒有進入後續維護的階段,其實非常的省時省力。但一旦踏入了維護的階段,看到那排山倒海而來的檔案......你可能會全身無力......
那使用Git呢? 由於Git是一個工具,我們在學習一樣工具如何使用的過程中多多少少會耗費一些學習的時間,但就我用到目前為止而言,覺得經濟效益確實比傳統的作法好上太多。使用Git需要使用命令工具列(也就是按壓鍵盤Windows+R後,輸入cmd會出現的東西)輸入一些命令,但有了Github提供的圖形化介面之後,其實已經好用很多。
使用Git的優缺點:
Pros
|
Cons
|
|
|
以前我用傳統的備份方式,有時候常常在不同版本的專案資料夾內把檔案拉來拉去,後來也忘記這些名字一樣的檔案到底哪個是有改過哪個是沒有改過的;或是說有時候不小心在系統詢問要不要取代時不小心按了取代,然後心血便付諸流水。
至少現在我用了Git,我能很肯定地說我可以很大膽的coding,我可以肆無忌憚的對程式碼大興土木,只要在每個關鍵點記得做一個記錄的動作,之後就算我改壞了,一行指令就回來啦。這時候你真的可以體會什麼叫作天無絕人之路、什麼叫作浪子回頭金不換XD
學習使用Git可以參考:
- 歐萊禮出版的版本控制使用Git(第二版),內文平易近人。
- 繁體中文的官方Git使用手冊,圖文並茂好消化。
註2:Git的commit
延伸閱讀:我的Git常用指令(準備中)
Solve the json encoding problem of Chinese characters in Java
今天想說要把資料從資料庫中擷取出來顯示在Android的使用者介面上
但因為我是使用php對MySQL做查詢
再將查詢的結果用陣列encode成json格式,再傳回Android
然後我有一個習慣就是我會先在Eclipse的console面板的log中先看傳回什麼東西XD
例如
後來花了一點時間查了一下資料,了解了這原來就是所謂的萬國碼(Unicode)
然後其實也不用花費時間在轉碼上焦頭爛額......因為
保險一點的話還是可以在語法中加入JSON_UNESCAPED_UNICODE
如此一來json在encode的過程中就不會強迫轉碼囉
但因為我是使用php對MySQL做查詢
再將查詢的結果用陣列encode成json格式,再傳回Android
然後我有一個習慣就是我會先在Eclipse的console面板的log中先看傳回什麼東西XD
例如
Log.e("result", json)
於是就看到我的log跑出一堆很奇怪的訊息,類似\u6211\u662f\u5e25\u54e5
就像這樣,每個都有\u開頭後來花了一點時間查了一下資料,了解了這原來就是所謂的萬國碼(Unicode)
然後其實也不用花費時間在轉碼上焦頭爛額......因為
就算log顯示的是那樣,Java在輸出(print、TextView)的時候還是會自動轉碼回去log的時候會產生這樣的結果,是因為json在encode的過程中會將中文自動轉碼
保險一點的話還是可以在語法中加入JSON_UNESCAPED_UNICODE
json_encode($string, JSON_UNESCAPED_UNICODE);
這是php版本5.4以後才有的功能如此一來json在encode的過程中就不會強迫轉碼囉
References
The Haversine formula
在我們設計App時,有時候我們會需要獲得使用者的地理位置資訊
然後用使用者目前的經緯度座標,計算離使用者地點最近的幾個地點
這時候如果你的資料庫中有存放一些地點的座標資料
那我們就可以使用所謂的Haversine公式,來算出地球上使用者與這些地點座標之間的距離

且資料表中的每個欄位記錄了每個地點的名稱(name)與座標(lat, lon),例如
接著我們可以將上面的公式轉換一下並且假設我們現在的座標在(23, 120)
在資料庫中就可以用這個語法來查詢
其中用到的acos()、cos()、sin()、radians()等函數,在MySQL中都可以直接使用
Haversine formula - Wikipedia, the free encyclopedia
Creating a Store Locator with PHP, MySQL & Google Maps - Google Maps API — Google Developers
然後用使用者目前的經緯度座標,計算離使用者地點最近的幾個地點
這時候如果你的資料庫中有存放一些地點的座標資料
那我們就可以使用所謂的Haversine公式,來算出地球上使用者與這些地點座標之間的距離
(簡單的解釋一下每個符號的意義:d是距離,r是弧度,φ是經度,λ是緯度)然後假設我們資料庫中有一個資料表(table)
且資料表中的每個欄位記錄了每個地點的名稱(name)與座標(lat, lon),例如
table
| ||
name
|
lat
|
lon
|
name_1
|
latitude_1
|
longitude_1
|
name_2
|
latitude_2
|
longitude_2
|
...
|
...
|
...
|
接著我們可以將上面的公式轉換一下並且假設我們現在的座標在(23, 120)
在資料庫中就可以用這個語法來查詢
SELECT
name,
( 6371 * acos( cos( radians( 23 ) ) * cos( radians( lat ) ) * cos( radians( lon )
- radians( 120 ) ) + sin( radians( 23 ) ) * sin( radians( lat ) ) ) ) AS distance
FROM
table
ORDER BY
distance
結果就會顯示這些地點的名稱與使用者之間的距離(公里),並且已經依照遠近排序其中用到的acos()、cos()、sin()、radians()等函數,在MySQL中都可以直接使用
Reference
Haversine formula - Wikipedia, the free encyclopedia
Creating a Store Locator with PHP, MySQL & Google Maps - Google Maps API — Google Developers
Public variables and methods
最近上數位學習的課,正在討論SCORM(Sharable Content Object Reference Model)
SCORM真的是個很神奇的傢伙!
它可以壓縮成package,接著上傳Moodle
然後學習者的一舉一動都會被監視XD
(例如:學習後進行測驗的分數、已經進行學習與否還有學習所耗費的時間等等)
其中談到SCORM的架構時,有說到SCORM使用了標準(standard)
來決定要儲存學習者的哪些資訊
也就是追蹤學習者學習的最小單位(SCOs)所用到的技術
在這過程中不小心就聯想到public變數與method的使用XD
所以就來複習一下吧 :P 這次要探討的是
所以說呢可以使用一些特別的方法(method),避免有心人士一次就把球球拿光光
或是在不小心的狀況之下本來要放4顆,結果放進了10顆(意外對變數造成修改)
在大多數情況下,我們都希望能夠自由地對變數進行操作。但是誠如Clean Code無瑕的程式碼一書所說:
SCORM真的是個很神奇的傢伙!
它可以壓縮成package,接著上傳Moodle
然後學習者的一舉一動都會被監視XD
(例如:學習後進行測驗的分數、已經進行學習與否還有學習所耗費的時間等等)
其中談到SCORM的架構時,有說到SCORM使用了標準(standard)
來決定要儲存學習者的哪些資訊
也就是追蹤學習者學習的最小單位(SCOs)所用到的技術
在這過程中不小心就聯想到public變數與method的使用XD
所以就來複習一下吧 :P 這次要探討的是
為什麼在修改資料時,與其直接操作public變數,另外建立method反而是更好的做法?
以下我將用箱子還有球來示範,應該是夠淺顯易懂吧~使用public變數
public class Box(){
//假設我們找來了一個箱子(它是一個物件)
//並且在裡面放進了5顆球,想說等一下要分別發送給5個人
int ball = 5;
}
然後...有人看到了這個箱子Box box = new Box();
box.ball = 0; //噢不...球球被拿光光了T_T
然後看看這個方式
public class Box(){
//一樣放進5顆球
private int ball = 5;
public Box()
//規定一次只能放一顆
public void getOnlyOneBall(){
ball = ball - 1;
}
}
之後有人要拿Box box = new Box();
box.getOnlyOneBall(); //一次只能拿一顆吧!你看看你~
Yes! 這次狀況就好很多了。所以說呢可以使用一些特別的方法(method),避免有心人士一次就把球球拿光光
或是在不小心的狀況之下本來要放4顆,結果放進了10顆(意外對變數造成修改)
在大多數情況下,我們都希望能夠自由地對變數進行操作。但是誠如Clean Code無瑕的程式碼一書所說:
「將實現的過程隱藏實際上便是一種抽象化的過程。而在這當中,類別不單單只是透過讀取及設定函式讓變數供人存取而已;To be more specific, 它提供了一個抽象介面,讓使用者在不需要知道實現過程的狀態下,還能操縱資料的本質。」改變世界的九大演算法一書中也有提到:
「撰寫程式碼的原則是盡量隱藏資料。」
Fragment切換,另一種方法
在之前的文章「Fragment切換起手式」中我已經介紹了最基本的方法,就是透過Framelayout作為container
然後使用FragmentTransaction的方式來更換Fragment
其實還存在另外一種方法,這邊我將使用Facebook的一個開放原始碼project來示範
main.xml
接著透過hide與show的方式控制Fragment出現的時機
MainActivity.class
不過有趣的一點是──在Activity執行生命週期的時候
所有的Fragment也會同時執行生命週期XDDDD
也就是說,若你有寫一些code,想要讓使用者載入該Fragment的時候自動執行
使用這個方法的結果就是──它同時載入了所有Fragment的code,並且同時執行
由於Facebook的這個project我認為它每個主要的Fragment其實沒有什麼太特殊的功能
不然的話我想使用這個方法必須經過再三考慮
facebook-android-sdk/samples/Scrumptious at master · facebook/facebook-android-sdk
然後使用FragmentTransaction的方式來更換Fragment
其實還存在另外一種方法,這邊我將使用Facebook的一個開放原始碼project來示範
main.xml
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<fragment android:name="com.facebook.scrumptious.SelectionFragment"
android:id="@+id/selectionFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<fragment android:name="com.facebook.scrumptious.SplashFragment"
android:id="@+id/splashFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<fragment android:name="com.facebook.widget.UserSettingsFragment"
android:id="@+id/userSettingsFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
一開始先將所有Fragment的layout都放置在主要Activity的layout中接著透過hide與show的方式控制Fragment出現的時機
MainActivity.class
FragmentManager fm = getSupportFragmentManager();
SplashFragment splashFragment = (SplashFragment) fm.findFragmentById(R.id.splashFragment);
// fragments[畫面名稱]是個用來存放相關畫面的Class類別陣列
// 如SPLASH、SELECTION、SETTINGS分別代表登入頁、主選單與設置頁
fragments[SPLASH] = splashFragment;
fragments[SELECTION] = fm.findFragmentById(R.id.selectionFragment);
fragments[SETTINGS] = fm.findFragmentById(R.id.userSettingsFragment);
FragmentTransaction transaction = fm.beginTransaction();
for(int i = 0; i < fragments.length; i++) {
transaction.hide(fragments[i]);
}
transaction.commit();
...
private void showFragment(int fragmentIndex, boolean addToBackStack) {
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction transaction = fm.beginTransaction();
//將不需要的頁面隱藏,只顯示想展示的頁面
for (int i = 0; i < fragments.length; i++) {
if (i == fragmentIndex) {
transaction.show(fragments[i]);
} else {
transaction.hide(fragments[i]);
}
}
if (addToBackStack) {
transaction.addToBackStack(null);
}
transaction.commit();
}
這種方式也是切換Fragment的一種方式,他同樣不會影響Activity的生命週期不過有趣的一點是──在Activity執行生命週期的時候
所有的Fragment也會同時執行生命週期XDDDD
也就是說,若你有寫一些code,想要讓使用者載入該Fragment的時候自動執行
使用這個方法的結果就是──它同時載入了所有Fragment的code,並且同時執行
由於Facebook的這個project我認為它每個主要的Fragment其實沒有什麼太特殊的功能
不然的話我想使用這個方法必須經過再三考慮
References
facebook-android-sdk/samples/Scrumptious at master · facebook/facebook-android-sdk
Fragment切換起手式
在Android版本3.0之後發行了Fragment這個新功能
從此之後就不用每次要開新的畫面就得launch新的Activity啦!
這個新功能可以做到同一個Activity內多個畫面的切換
同時又不影響整個Activity的生命週期(同場加映Fragment與Activity互相對應的生命週期)
以下將展示Fragment切換起手式
首先你會準備幾個檔案:
Fragments | Android Developers
從此之後就不用每次要開新的畫面就得launch新的Activity啦!
這個新功能可以做到同一個Activity內多個畫面的切換
同時又不影響整個Activity的生命週期(同場加映Fragment與Activity互相對應的生命週期)
以下將展示Fragment切換起手式
首先你會準備幾個檔案:
- 執行以下代碼的Activity(這邊以MainActivity.class為例)
- 欲更換的Fragment(這邊以FragmentNearby.class為例)
- 整個畫面的layout(這邊以main.xml為例,在欲更換Fragment的地方放置Framelayout元件,且在本例中該元件id為content)
// new一個新的Fragment類別物件
FragmentNearby fragmentNearby = new FragmentNearby();
// getFragmentManager, 這邊getSupportFragmentManager是為了向較舊的版本相容
FragmentManager fragmentManager = getSupportFragmentManager();
// 進行transaction
FragmentTransaction transaction = fragmentManager.beginTransaction();
// 對Fragment動手動腳,指令額外包括add, remove, hide, show...etc
transaction.replace(R.id.content, fragmentNearby);
// 確認並執行transaction!
transaction.commit();
References
Fragments | Android Developers
Sending arguments via Android, php, and external java
經由Android傳遞數值經過php再到伺服器端執行java程式後再返回Android的架構
傳遞數值的關鍵步驟大概如下面所列出:
- (Android)以EditText取得使用者輸入的數值,並以HTTP POST方式傳輸給php
- (php)取得使用者裝置傳來的數值,將數值傳入Java程式並執行
- (Java)執行結束後將結果返回給php
- (php)取得結果後以json格式封裝,傳回給使用者裝置
- (Android)取得結果,解碼json
1.(Android)以EditText取得使用者輸入的數值,並以HTTP POST方式傳輸給php
取得使用者inputString input = editText.getText.toString
將input封裝到params這個List裡params.add(new BasicNameValuePair("input", input));
用HTTP POST的方式送出資料httpPost.setEntity(new UrlEncodedFormEntity(params));
2.(php)取得使用者裝置傳來的數值,將數值傳入Java程式並執行
取得使用者input$input= $_POST[‘input’];
執行外部程式,並代入參數exec(“java –jar path/to/file.jar input”, $output);
3.(Java)執行結束後將結果返回給php
執行Java
取得InputStream的內容public static void main(String[] args){
String input = args[0];
(中間愛幹嘛就幹嘛)
System.out.print(結果);
}
4.(php)取得結果後以json格式封裝,傳回給使用者裝置
取得Java傳回來的結果(它是陣列)$result = output[0];
以json格式封裝die(json_encode($result));
5.(Android)取得結果,解碼json
Is=httpEntity.getContent();
最後就可以使用BufferReader與JSONObject的方法使用結果了!HttpClient起手式
當我們用瀏覽器(像是IE、Chrome、Firefox)在逛網站時
可以想像我們的電腦(客戶端,Client)要和網站的電腦(伺服端,Server)一直都在進行溝通
進行溝通的方式大概就是client對server發出要求(request),然後server送回應(response)給client
接著client再想辦法去解析response中所夾帶的資料~
那通常送出request有兩種方法,分別為POST和GET
兩者之間有一點點不同,但這不是這次要講的內容......
這次要說的是Android和網頁或網站傳遞資料的過程當中
所會用到的HttpClient與相關的常用程式碼寫法:
關於params(傳遞參數,parameters)的設定,它必須使用NameValuePair格式儲存
延伸問題一、HttpClient好像也很常見,和DefaultHttpClient的差別?
DefaultHttpClient (Apache HttpClient 4.3.6 API)
DefaultHttpClient is deprecated
【Android进阶学习】Http编程之HttpClient
可以想像我們的電腦(客戶端,Client)要和網站的電腦(伺服端,Server)一直都在進行溝通
進行溝通的方式大概就是client對server發出要求(request),然後server送回應(response)給client
接著client再想辦法去解析response中所夾帶的資料~
那通常送出request有兩種方法,分別為POST和GET
兩者之間有一點點不同,但這不是這次要講的內容......
這次要說的是Android和網頁或網站傳遞資料的過程當中
所會用到的HttpClient與相關的常用程式碼寫法:
HttpClient起手式
//new一個新的HttpClient物件
DefaultHttpClient httpClient = new DefaultHttpClient();
//看是要用POST或GET方法,new一個出來,並且指定網址(url)
HttpPost httpPost = new HttpPost(url);
//將要傳遞的參數放到params裡面並設定HttpPost的Entity
httpPost.setEntity(new UrlEncodedFormEntity(params));
//new一個HttpResponse來裝httpClient執行之後的結果
HttpResponse httpResponse = httpClient.execute(httpPost);
//使用HttpEntity把內容拿出來
HttpEntity httpEntity = httpResponse.getEntity();
//(範例)內容可以應用在InputStream中
is = httpEntity.getContent();
關於params(傳遞參數,parameters)的設定,它必須使用NameValuePair格式儲存
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("username", username));
params.add(new BasicNameValuePair("password", password));
延伸問題一、HttpClient好像也很常見,和DefaultHttpClient的差別?
DefaultHttpClient是一個類別(Class),HttpClient是一個介面(Interface)
基本上藉由對HttpClient的實作已經可以應付大部分的狀況 (Default implementation of HttpClient pre-configured for most common use scenarios)
DefaultHttpClient之後也不建議使用了(Deprecated),可以使用HttpClientBuilder、CloseableHttpClient等其他方法
延伸問題二、和透過PHP表單輸入的差別?
在PHP中得透過$_POST["name"]的方式來取得表單中的數值,這邊則得要設定params
References
DefaultHttpClient (Apache HttpClient 4.3.6 API)
DefaultHttpClient is deprecated
【Android进阶学习】Http编程之HttpClient
Jena常用指令
資訊來自Jena javadoc,我將比較常用的翻譯一下
如果沒翻譯的可能就是比較不重要的,或是我現在用不到的。
如果沒翻譯的可能就是比較不重要的,或是我現在用不到的。
Model
createProperty
Property createProperty(String nameSpace, String localName)
Create a property with a given URI composed from a namespace part and a localname part by concatenating the strings.This method may return an existing property with the correct URI and model, or it may construct a fresh one, as it sees fit.
Subsequent operations on the returned property may modify this model.- Parameters:
nameSpace- Property的命名空間。localName- 你幫該property取的名字。- Returns:
- a property instance
Resouce
標籤:
Semantic Web
Jena SPARQL
SPARQL是用來查詢RDF graph的一個語法。
RDF graph就是一大堆triple的組成
在Jena中所提到的model,也就是RDF graph,statement就是triples
RDF dataset中則包含了更多的graph
若發生JENA_HOME not set
就是環境變數沒有設定
1.先echo %PATH%,看看目前有沒有紀錄到
2.沒有的話入PATH=C:\Program Files\Java\(JDK資料夾)\bin;%PATH%
3.完成!!!!!!!!!!!!!
no query string or query file
bat\sparql.bat --data=vc-db-1.rdf --query=q1.rq
!bound(?age) 這是一個unbound的動作,意即不符合結果的資料也會列出
SPARQL Tutorial
Tutorial - Manipulating SPARQL using ARQ
RDF graph就是一大堆triple的組成
在Jena中所提到的model,也就是RDF graph,statement就是triples
RDF dataset中則包含了更多的graph
SPARQL設定方面
command Line的部分 使用cd C:\位址 跳槽若發生JENA_HOME not set
就是環境變數沒有設定
1.先echo %PATH%,看看目前有沒有紀錄到
2.沒有的話入PATH=C:\Program Files\Java\(JDK資料夾)\bin;%PATH%
3.完成!!!!!!!!!!!!!
no query string or query file
bat\sparql.bat --data=vc-db-1.rdf --query=q1.rq
把兩個檔案放到同目錄就好了
基本用法
基本用法
SELECT ?欄位 WHERE { 條件(必須是triple) }
String Matching
{FILTER regex(?g, "r", "i") } "r"是你想查詢的字串
Testing Values
SELECT ?resource WHERE { ?resource info:age ?age . FILTER (?age >= 24) 使用FILTER(條件) }
OPTIONAL(IF)
SELECT ?name ?age WHERE { ?person vcard:FN ?name . OPTIONAL { ?person info:age ?age } }
在prefix前面有小老鼠的可能是n3格式,把rdf改成n3就可以解碼,如
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
UNION
PREFIX foaf: <http://xmlns.com/foaf/0.1/> PREFIX vCard: <http://www.w3.org/2001/vcard-rdf/3.0#> SELECT ?name WHERE { { [] foaf:name ?name } UNION { [] vCard:FN ?name } }
其實sparql的語法很free,也可以修改成這樣
PREFIX foaf: <http://xmlns.com/foaf/0.1/> PREFIX vCard: <http://www.w3.org/2001/vcard-rdf/3.0#> SELECT ?name WHERE { [] ?p ?name FILTER ( ?p = foaf:name || ?p = vCard:FN ) }
如果把第一個例子的?name改成?name1與?name2,那結果可以更明顯的顯示
PREFIX foaf: <http://xmlns.com/foaf/0.1/> PREFIX vCard: <http://www.w3.org/2001/vcard-rdf/3.0#> SELECT ?name1 ?name2 WHERE { { [] foaf:name ?name1 } UNION { [] vCard:FN ?name2 } } --------------------------------- | name1 | name2 | ================================= | "Matt Jones" | | | "Sarah Jones" | | | | "Becky Smith" | | | "John Smith" | ---------------------------------
Datasets
輸入sparql --help查看全部指令,可以看到
Dataset--data=FILE Data for the datset - triple or quad formats--graph=FILE Graph for default graph of the datset(指定dataset中的預設graph)--namedGraph=FILE Add a graph into the dataset as a named graph(對graph命名,並且加入dataset)
列出所有資料,語法sparql --graph ds-dft.ttl --namedgraph ds-ng-1.ttl --named graph ds-ng-2.ttl --query xxxxx.rq
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> PREFIX dc: <http://purl.org/dc/elements/1.1/> PREFIX : <.> SELECT * { { ?s ?p ?o } UNION { GRAPH ?g { ?s ?p ?o } } }
另外用法,注意GRAPH後面一定要先留空格
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> PREFIX dc: <http://purl.org/dc/elements/1.1/> PREFIX : <.> SELECT * { { ?s ?p ?o } UNION { GRAPH :ds-ng-1.ttl { ?s ?p ?o } } 列出和ds-ng-1.ttl聯集資料 }
也可以把指令改寫成--query xxxxx.rq 只是要把FROM與FROM NAMED加入取代--graph與--namedgraph
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> PREFIX dc: <http://purl.org/dc/elements/1.1/> PREFIX : <.> SELECT * FROM <ds-dft.ttl> FROM NAMED <ds-ng-1.ttl> FROM NAMED <ds-ng-2.ttl> { { ?s ?p ?o } UNION { GRAPH ?g { ?s ?p ?o } } }
也可以combo FILTER來使用
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> PREFIX dc: <http://purl.org/dc/elements/1.1/> PREFIX : <.> SELECT ?date ?title { ?g dc:date ?date . FILTER (?date > "2005-08-01T00:00:00Z"^^xsd:dateTime ) GRAPH ?g { ?b dc:title ?title } }
Manipulating SPARQL using ARQ
ARQ使用JAVA的方式來操作SPARQL(施工中)References:
SPARQL Tutorial
Tutorial - Manipulating SPARQL using ARQ
標籤:
Semantic Web
Jena RDF Core API
Jena是使用Java編寫的API,可以用來做本體論的新增、查詢、修改與推論。
若還沒有建立Jena環境,可以參考如何在Eclipse中建立第一個Jena專案
以下在Jena中所提到的model、statement分別可以對應到RDF graph與triple。triple(statement)通常以名詞──動詞──名詞(主詞subject、謂詞predicate、受詞object)的方式表示,例如
Shakespear write Camelot
graph則是一大堆triple的集合。
簡單操作
1.建立Model
從ModelFactory中創造預設的ModelModel model = ModelFactory.createDefaultModel();
2.從Model中建立Resource
Resource johnSmith = model.createResource(personURI);
3.對resource增加property
把所有的triple列出來
1.使用Statement Iterator
StmtIterator iter = model.listStatements();
2.在while迴圈中建立Statement
Statement stmt = iter.nextStatement();
3.抓出getter
Resource subject = stmt.getSubject(); // get the subject
Property predicate = stmt.getPredicate(); // get the predicate
RDFNode object = stmt.getObject(); // get the object
4.也可以直接輸出XML格式
model.write(System.out);
Triple-annotation格式
model.write(System.out, "N-TRIPLES");
讀取rdf檔案
1.建立Model
Model model = ModelFactory.createDefaultModel();
2.使用FileManager打開檔案
InputStream in = FileManager.get().open( inputFileName );
3.列印XML
model.read(in, null);
model.write(System.out);
P.S.:找不到vc-db-1.rdf? 放在和你放jena資料夾一樣的地方就好囉
設定前置命名空間
1.建立Model
Model model = ModelFactory.createDefaultModel();
2.設定前置
String nsA = "http://somewhere/else#";
String nsB = "http://nowhere/else#";
3.產生resources, properties,
Resource root = m.createResource( nsA + "root" );//類似○,代表subject與objectProperty P = m.createProperty( nsA + "P" );//類似→,代表predicateResource x = m.createResource( nsA + "x" );//類似○,代表subject與object
4.將resource與property間的關係建立起來
m.add( root, P, x );
5.查看結果
m.write( System.out );
PS.:有沒有m.setNsPrefix的差異如下:
<j.1:P rdf:resource="http://somewhere/else#x"/> //no set
<nsA:P rdf:resource="http://somewhere/else#x"/> //set
m.read("location")
http://stackoverflow.com/questions/15782656/reading-local-owl-file-using-jena-response-filenotfoundexception
抽取URI
1.新增一個Resource, 存放等一下要retrive的resource
Resource vcard = model.getResource("http://somewhere/JohnSmith/");;
2.對Resource使用getProperty,把資料抽出來
String fullName = vcard.getProperty(VCARD.FN).getString();
PS.:這邊getString會直接輸出資料值,使用toString會輸出一整串XD
3.可以對取出的Resource增加資料
vcard.addProperty(VCARD.NICKNAME, "Smithy")
4.使用StmtIterator 來把所有的property列出來
StmtIterator iter = vcard.listProperties(VCARD.NICKNAME);
查詢Model
這邊是說如果什麼事情都用model.listStatement()來做也就太累了
1.所以使用ResIterator來代替Resource的查詢,找出有VCARD:FN屬性的資源
ResIterator iter = model.listSubjectsWithProperty(VCARD.FN);
2.進入if 迴圈 iter.hasNext print
if (iter.hasNext()) {
System.out.println("The database contains vcards for:");
while (iter.hasNext()) {
System.out.println(" " + iter.nextResource()
.getProperty(VCARD.FN)
.getString());
}
有另外的方法,使用new SimpleSelector
1.再seletor中分別填入S, P, O
StmtIterator iter = model.listStatements(new SimpleSelector(null, VCARD.FN, (RDFNode) null)
操作Model
關於union, intersection與difference的操作
Model model = model1.union(model2);
容器(container)
關於容器,有三種,分別是BAG, ALT, SEQ
BAG是沒有規則隨便亂放的
ALT雖然也是沒有規則,但是基本上用來放替代品
標籤:
Semantic Web
訂閱:
文章 (Atom)



