好久沒有寫blog了,最近似乎變懶了。今天上班沒有很多事,于是把之前遇到的一個問題記錄下來。
web開發會涉及到很多類型轉換的情況。我們知道,頁面中的一切值都是字元串類型,而到背景,我們需要的可能是其他各種類型;同時,頁面顯示也是字元串類型。這就涉及到web中基本的類型轉換問題:從string轉換為各種類型與從各種類型轉換為string類型。
1、在servlet中,這一切的轉換我們得自己寫代碼完成;
2、在struts1.x中,我們通過apache commons-beanutils中的converters來幫助完成這些事情;
3、在struts2中,使用的則是基于ongl的類型轉換;
……
日期類型的轉換,web開發中幾乎都會遇到,我現在做得項目也不例外。在開發的過程中,也許就像你一樣,我沒有對日期類型的轉換做任何特殊的處理,而且struts2也很好的幫我完成了轉換。然而同僚測試的時候卻出現了一個“莫名其妙”的問題:輸入一個常用格式的日期類型yyyy-mm-dd,到背景卻報錯:找不到對應的set方法——seteffdate(ljava.lang.string)。的确,程式中隻有seteffdate(java.util.date)這個方法,沒有seteffdate(ljava.lang.string)這個方法。從ljava.lang.string可以看出,傳到背景的string類型并沒有轉換成date類型,因而報錯。
一開始,我以為是我ut沒做好,于是在自己的電腦上模拟同僚的測試,結果一點問題也沒有。這就奇怪了。經過自己分析,覺得可能是ie浏覽器的原因,因為同僚測試用的是ie,而我用的是firefox。于是在自己的機子上用ie測試,同時在同僚機子上用firefox測試,結果這兩次測試都沒有出現上面的問題。雖然沒有找到問題所在,但可以初步肯定:ie的問題,但似乎又不完全是ie的問題,因為在我的電腦上的ie(版本與同僚一樣,都是ie6)沒有上述問題。這就奇怪了,是什麼問題呢,真是百思不得其解。
這個時候,我想起了之前遇到的一個不解得情況:從背景獲得的日期類型在頁面上顯示時,跟上面情況一樣,在同僚的ie中,日期顯示的格式竟然是:yyyy-mm-ddthh:mm:ss。多了一個t,真是莫名其妙,而且隻在同僚的ie浏覽器中出現(當時解決方法是在js中将't'替換為空格,沒有去深究,但現在必須的深究了)。yyyy-mm-ddthh:mm:ss這種日期格式有嗎?于是查詢jdk:在simpledateformat類中找到了該日期格式,這種格式是“美國語言環境中日期和時間的模式之一”。原來還真有這種格式。竟然這是美國語言中使用的日期格式,而struts2是美國人開發的,也許跟這個有點關系。于是檢視struts2中關于date類型轉換的源碼。
在xworkbasicconverter類中
private object doconverttodate(map<string, object> context, object value, class totype) {
date result = null;
if (value instanceof string && value != null && ((string) value).length() > 0) {
string sa = (string) value;
locale locale = getlocale(context);
dateformat df = null;
if (java.sql.time.class == totype) {
df = dateformat.gettimeinstance(dateformat.medium, locale);
} else if (java.sql.timestamp.class == totype) {
date check = null;
simpledateformat dtfmt = (simpledateformat) dateformat.getdatetimeinstance(dateformat.short,
dateformat.medium,
locale);
simpledateformat fullfmt = new simpledateformat(dtfmt.topattern() + millisecond_format,
simpledateformat dfmt = (simpledateformat) dateformat.getdateinstance(dateformat.short,
simpledateformat[] fmts = {fullfmt, dtfmt, dfmt};
for (simpledateformat fmt : fmts) {
try {
check = fmt.parse(sa);
df = fmt;
if (check != null) {
break;
}
} catch (parseexception ignore) {
}
}
} else if (java.util.date.class == totype) {
dateformat[] dfs = getdateformats(locale);
for (dateformat df1 : dfs) {
check = df1.parse(sa);
df = df1;
catch (parseexception ignore) {
}
//final fallback for dates without time
if (df == null) {
df = dateformat.getdateinstance(dateformat.short, locale);
try {
df.setlenient(false); // let's use strict parsing (xw-341)
result = df.parse(sa);
if (!(date.class == totype)) {
constructor constructor = totype.getconstructor(new class[]{long.class});
return constructor.newinstance(new object[]{long.valueof(result.gettime())});
} catch (exception e) {
throw new xworkexception("couldn't create class " + totype + " using default (long) constructor", e);
} catch (parseexception e) {
throw new xworkexception("could not parse date", e);
} else if (date.class.isassignablefrom(value.getclass())) {
result = (date) value;
}
return result;
}
private dateformat[] getdateformats(locale locale) {
dateformat dt1 = dateformat.getdatetimeinstance(dateformat.short, dateformat.long, locale);
dateformat dt2 = dateformat.getdatetimeinstance(dateformat.short, dateformat.medium, locale);
dateformat dt3 = dateformat.getdatetimeinstance(dateformat.short, dateformat.short, locale);
dateformat d1 = dateformat.getdateinstance(dateformat.short, locale);
dateformat d2 = dateformat.getdateinstance(dateformat.medium, locale);
dateformat d3 = dateformat.getdateinstance(dateformat.long, locale);
dateformat rfc3399 = new simpledateformat("yyyy-mm-dd't'hh:mm:ss");
dateformat[] dfs = {dt1, dt2, dt3, rfc3399, d1, d2, d3}; //added rfc 3339 date format (xw-473)
return dfs;
其中short、medium、long在jdk中的dateformat類中有說明。
從這句dateformat rfc3399 = new simpledateformat("yyyy-mm-dd't'hh:mm:ss");可以看出,struts2寫死使用了這樣一種格式。然而,struts2中這種格式是放在最後的,為啥隻有同僚的ie浏覽器測試時使用的是這種格式呢?(在調試時,用同時ie,日期輸入中按這種格式輸入,還真的沒有問題了)這說明,同時的電腦中,前面六種dateformat都沒有比對,檢視dateformat中關于short、medium、long的說明,可以知道,對于yyyy-mm-dd這種日期類型,在英語語言中是沒法比對的,由于struts2比對日期時,使用了locale,可見,同僚的ie浏覽器預設的語言環境是英語。一經檢視,果然如此,把中文設定為預設語言環境,再測試,沒問題了。終于知道了原因。
個人覺得,struts2中,最後一種日期模式寫死成美國标準,不是很好。
針對這個問題,我們沒法要求客戶一定設定中文為預設浏覽器的語言環境。因而對于date類型的轉換,可以自己定義一個轉換器。以下來自http://www.javaeye.com/wiki/struts2/1365-passing-parameters-in-struts2 中的一個類型轉換器定義(不适合國際化的環境),如需要,你可以定義自己的轉換器:
public class dateconverter extends defaulttypeconverter {
private static final logger logger = logger.getlogger(dateconverter.class);
private static final string datetime_pattern = "yyyy-mm-dd hh:mm:ss";
private static final string date_pattern = "yyyy-mm-dd";
private static final string month_pattern = "yyyy-mm";
/**
* convert value between types
*/
@suppresswarnings("unchecked")
public object convertvalue(map ognlcontext, object value, class totype) {
object result = null;
if (totype == date.class) {
result = doconverttodate(value);
} else if (totype == string.class) {
result = doconverttostring(value);
* convert string to date
*
* @param value
* @return
private date doconverttodate(object value) {
if (value instanceof string) {
result = dateutils.parsedate((string) value, new string[] { date_pattern, datetime_pattern, month_pattern });
// all patterns failed, try a milliseconds constructor
if (result == null && stringutils.isnotempty((string)value)) {
try {
result = new date(new long((string) value).longvalue());
} catch (exception e) {
logger.error("converting from milliseconds to date fails!");
e.printstacktrace();
} else if (value instanceof object[]) {
// let's try to convert the first element only
object[] array = (object[]) value;
if ((array != null) && (array.length >= 1)) {
value = array[0];
result = doconverttodate(value);
* convert date to string
private string doconverttostring(object value) {
simpledateformat simpledateformat = new simpledateformat(datetime_pattern);
string result = null;
if (value instanceof date) {
result = simpledateformat.format(value);
}
可以将該轉換器注冊為全局的:在classpath下建立xwork-conversion.properties檔案,内容為:java.util.date=你的類型轉換器的完整限定類名