Apache POI Excel入力規則の読み込み(1)

Apache POI で Excel の入力規則によるプルダウンの情報を読み取る場合、同じシート内のセルで入力規則の値を格納していないと読み取れない。

f:id:posturan:20160521212235j:plain

f:id:posturan:20160521212317j:plain

このように同じシート内にある入力規則の情報を参照するコードサンプルを書いてみた。
(ただし、このサンプルは縦方向リストのみ)

try(InputStream is = new FileInputStream("template.xlsx");){
	XSSFWorkbook book = new XSSFWorkbook(is);
	XSSFSheet sheet = book.getSheetAt(0);
	/* List<XSSFDataValidation> を取得して展開  */
	sheet.getDataValidations().stream().forEach(e->{
		// CellRangeAddress[] を取得して入力規則適用されてるセルのリファレンスを参照
		Arrays.stream(e.getRegions().getCellRangeAddresses()).forEach(r->{
			String region_start_cellrefer = sheet.getRow(r.getFirstRow()).getCell(r.getFirstColumn()).getReference();
			String region_end_cellrefer = sheet.getRow(r.getLastRow()).getCell(r.getLastColumn()).getReference();
			System.out.println("対象セル:" + region_start_cellrefer + " - " + region_end_cellrefer );
		});
		// 入力時のタイトルとメッセージ
		System.out.println("title = "+ e.getPromptBoxTitle() );
		System.out.println("text  = "+ e.getPromptBoxText() );
		// 入力規則 DataValidationConstraint を取得して、入力規則リスト格納領域を getExplicitListValues() で取得して参照
		Arrays.stream(e.getValidationConstraint().getExplicitListValues()).forEach(p->{
			System.out.println("Excel の 入力規則リスト値格納セル:" +p);
			String[] ary = p.replaceAll("\\$", "").split(":");
			int startRow = Integer.parseInt(ary[0].replaceAll("[A-Z]+", "")) - 1;
			int endRow   = Integer.parseInt(ary[1].replaceAll("[A-Z]+", "")) - 1;
			int startCol = CellReference.convertColStringToIndex(ary[0].replaceAll("[0-9]+", ""));
			int endCol   = CellReference.convertColStringToIndex(ary[1].replaceAll("[0-9]+", ""));
			if (startRow==endRow){
				IntStream.rangeClosed(startCol, endCol).forEach(i->{
					XSSFCell cell = sheet.getRow(startRow).getCell(i);
					if (cell.getCellType()==Cell.CELL_TYPE_NUMERIC){
						Integer data = new Double(cell.getNumericCellValue()).intValue();
						System.out.println(data);
					}else if(cell.getCellType()==Cell.CELL_TYPE_STRING){
						String data = cell.getStringCellValue();
						System.out.println(data);
					}
				});
			}else{
				IntStream.rangeClosed(startRow, endRow).forEach(i->{
					XSSFCell cell = sheet.getRow(i).getCell(startCol);
					if (cell.getCellType()==Cell.CELL_TYPE_NUMERIC){
						Integer data = new Double(cell.getNumericCellValue()).intValue();
						System.out.println(data);
					}else if(cell.getCellType()==Cell.CELL_TYPE_STRING){
						String data = cell.getStringCellValue();
						System.out.println(data);
					}
				});
			}
		});
		System.out.println("------------------------------------");
	});
}catch(Exception e){
	e.printStackTrace();
}

結果としては、以下のような標準出力結果が得られる。

対象セル:D5 - D5
title = タイトル1
text = メッセージ1
Excel の 入力規則リスト値格納セル:$L$2:$L$6
A
B
C
D
E
'--------------------------------'
対象セル:E5 - E5
対象セル:G5 - G5
title = タイトル2
text = メッセージ2
Excel の 入力規則リスト値格納セル:$M$2:$M$6
100
200
300
400
500
'--------------------------------'

Reloading this page will cause modal window to disappear を回避

Wicket の モーダルウィンドウを閉じる時に、ページ遷移 setResponsePage 

を何も対処せずに実行すると、、

  Reloading this page will cause modal window to disappear

が発生してしまう。

これを回避するには、モーダルウィンドウを設置するページで初期処理として

以下の JavaScript を実行しておくことだ。

  Wicket.Window.unloadConfirmation = false;

 

GSON でjson生成時の null を処理する(2)

先日書いたGSON使用時の  String型のNULLパターン { "aaa": "null" } でなくて、

{ "aaa": "" } にするための TypeAdapterFactory を書き直しました。

import java.io.IOException;
import com.google.gson.Gson;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
/**
 * NullStringToEmptyAdapterFactory
 */
public class NullStringToEmptyAdapterFactory implements TypeAdapterFactory{
   @Override
   public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
      Class<T> rawType = (Class<T>)type.getRawType();
      if (rawType != String.class){
          return null;
      }
      return (TypeAdapter<T>) new TypeAdapter<String>(){
         @Override
         public String read(JsonReader reader) throws IOException{
            if (reader.peek()==JsonToken.NULL){
               reader.nextNull();
               return "";
            }
            return reader.nextString();
         }
         @Override
         public void write(JsonWriter writer, String value) throws IOException{
            if (value==null){
               writer.value("");
               return;
            }
            writer.value(value);
         }
      };
   }
}
Integer型も、対応するなら、ちょっと長くなりますが、、、

import java.io.IOException;
import com.google.gson.Gson;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
/**
 * NullStringIntegerToEmptyAdapterFactory
 */
public class NullStringIntegerToEmptyAdapterFactory implements TypeAdapterFactory{
   @Override
   public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
      Class<T> rawType = (Class<T>)type.getRawType();
      if (rawType != String.class && rawType != Integer.class){
          return null;
      }
      if (rawType==Integer.class){
         return (TypeAdapter<T>) new TypeAdapter<Integer>(){
            @Override
            public Integer read(JsonReader reader) throws IOException {
               if (reader.peek()==JsonToken.NULL){
                  reader.nextNull();
                  return null;
               }
               String s = reader.nextString();
               return s==null || "".equals(s) ? null : new Integer(s);
            }
            @Override
            public void write(JsonWriter writer, Integer value) throws IOException {
               if (value==null){
                  writer.value("");
                  return;
               }
               writer.value(value.toString());
            }
         };
      }
      return (TypeAdapter<T>) new TypeAdapter<String>(){
         @Override
         public String read(JsonReader reader) throws IOException{
            if (reader.peek()==JsonToken.NULL){
               reader.nextNull();
               return "";
            }
            return reader.nextString();
         }
         @Override
         public void write(JsonWriter writer, String value) throws IOException{
            if (value==null){
               writer.value("");
               return;
            }
            writer.value(value);
         }
      };
   }
}

GSON でjson生成時の null を処理する場合、

google  GSON でJSONを生成する場合、そのままだと、nullオブジェクトは無視(出力されない)なので、GsonBuilder の serializeNulls() を呼び出して、  { "a": "null" } のように出す。

しかし、String オブジェクトの結果として、{ "a": "" } が欲しい場合がある。

JsonSerializer<String> を実装した adapter を書いてみたが、うまくいかない。

古い情報だが、

http://stackoverflow.com/questions/9483348/gson-treat-null-as-empty-string

にヒントがあった。当時のバージョンよりバージョンUPが期待されていたが、2.6.2でも一向に解決されてない。stackoverflow に書いてあった TypeAdapterFactoryの方法が最も有効で、stackoverflowに書いてあったものを修正すると思いどおりになる。

 

import java.io.IOException;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
/**
 * StringAdapter
 */
public class StringAdapter extends TypeAdapter{
   @Override
   public String read(JsonReader reader) throws IOException{
      if (reader.peek() == JsonToken.NULL){
         reader.nextNull();
         return "";
      }
      return reader.nextString();
   }
   @Override
   public void write(JsonWriter writer, String value) throws IOException {
      if (value == null) {
         writer.value("");
         return;
      }
      writer.value(value);
   }
}

public class NullStringToEmptyAdapterFactory implements TypeAdapterFactory{
    public  TypeAdapter create(Gson gson, TypeToken type){
       Class rawType = (Class) type.getRawType();
	   if (rawType != String.class){
			 return null;
		}
		return (TypeAdapter) new StringAdapter();
	}
}
この TypeAdapterFactory の create で無名クラスで返すように書きたかったのだが、 

うまく書けなかった。

Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(new NullStringToEmptyAdapterFactory())
.create();

でも、Google group の投稿には、gsonで作成済の json 文字列に対して、replaceAll("nill", "\"\"") を実行すればいいじゃないのように、書いてある。

いや、確かにそうなんだけど。。。。

それなら、replaceAll(":\"null\"", "\"\"") って書くけど。。。replaceAll がもし負荷が上がるなら、TypeAdpter の方で処理するのが正解なのか。。。。

Wicket AjaxButton onSubmit の前に JavaScript を差し込む

Wicket の AjaxButton  onSubmit の実行前に処理したいJavaScript がある場合、

わざわざ jQuery$( ボタンセレクタ ) .click( function(){ ...} ); のソースを用意して

JavaScriptHeaderItem.forReference で読み込ませる方法にするか、

生成したAjaxButton に、更に、 AjaxEventBehavior("click") の onEvent で、

  AjaxRequestTarget # prependJavaScript で書くか。。

が通常、思いつく方法だが、

AjaxButton  の中の getOnClickScript メソッドをオーバーライドして、

クリック時に実行したい JavaScript を渡して JavaScript 処理後にonSubmitの処理が

走る方法もある。

           @Override

           protected String getOnClickScript(){

                    return "$('#name').val('" + foo + "')";

            }

 

GSON と LocalDate / LocalDateTime のシリアライズ・デシリアライズ

Java7をやめてJava8で開発するようになり、java.util.Dateを使わなくなり、

Google gson で LocalDate / LocalDateTime をシリアライズ・デシリアライズするのに

簡単に書ける方法を探すのに苦労したのでその過程と結果です。

GsonBuilder の registerTypeAdapter で LocalDateのシリアライズ&デシリアライズ

LocalDateTimeのシリアライズ&デシリアライズと、registerTypeAdapter の呼び出しを4回も書かなくてはならない。

  new GsonBuilder().registerTypeAdapter(LocalDate.class, new JsonSerializer<LocalDate>(){

            @Override
              public JsonElement serialize(LocalDate date, Type type, JsonSerializationContext context){
                  return new JsonPrimitive(date.format(DateTimeFormatter.ofPattern("yyyy/MM/dd")));
              }
          }).  /* このようなものを、毎回4つ書くのは辛い。。*/

そこで、以下のような Adapter を2つ用意する。

 

@FunctionalInterface
public interface LocalDateAdapter extends JsonSerializer<LocalDate>, JsonDeserializer<LocalDate> {
@Override
public default JsonElement serialize(LocalDate date, Type type, JsonSerializationContext context){

return new JsonPrimitive(date.format(getFormatter()));
}
@Override
public default LocalDate deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException{

return LocalDate.parse(json.getAsString(), getFormatter());
}

public static LocalDateAdapter create(LocalDateAdapter a){
return a;
}

public DateTimeFormatter getFormatter();

}

続きを読む