001/* 002 * Copyright (c) 2009 The openGion Project. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 013 * either express or implied. See the License for the specific language 014 * governing permissions and limitations under the License. 015 */ 016package org.opengion.plugin.io; 017 018import java.io.PrintWriter; 019import java.util.List; 020import java.util.Locale; 021import java.util.Map ; 022import java.util.LinkedHashMap ; 023 024import org.opengion.fukurou.util.HybsEntry; 025import org.opengion.fukurou.util.StringUtil; // 8.5.6.1 (2024/03/29) 026import org.opengion.hayabusa.db.DBTableModel; 027 028/** 029 * TableWriter をJSON形式で出力する為の実装クラスです。 030 * DefaultTableWriter を継承していますので、ラベル、名前、データの出力部のみ 031 * オーバーライドして、JSON形式ファイルの出力機能を実現しています。 032 * 033 * 出力のJSON形式(JavaScript Object Notation)は、JavaScriptにおける 034 * オブジェクトの表記法をベースとした軽量なデータ記述言語です。 035 * このクラスでは、基本的にはすべてを文字列として処理しますので、 036 * 数字や、true,false も、ダブルコーテーションでくくることになります。 037 * ただし、null の場合は、null 表記になりますが、通常 空文字 "" になる場合もあります。 038 * 1レコードのみの場合でも、配列要素として取り扱います。 039 * UTF-8 でエンコードし、MIMEタイプはapplication/json、拡張子はjson とするのが一般的です。 040 * 041 * [ 042 * { "カラム1":"値1" , "カラム2":"値2" , … } , 1レコード目 043 * { "カラム1":"値1" , "カラム2":"値2" , … } , 2レコード目 044 * ・・・・ 045 * { "カラム1":"値1" , "カラム2":"値2" , … } Nレコード目 046 * ] 047 * 048 * writeTableParamタグで、key="JsonName" value="パラメータ名" を指定すると、 049 * JSON形式で、配列をオブジェクトとしてまとめるパラメータを指定する事が可能です。 050 * 051 * { 052 * パラメータ名:[ 053 * { "カラム1":"値1" , "カラム2":"値2" , … } , 1レコード目 054 * { "カラム1":"値1" , "カラム2":"値2" , … } , 2レコード目 055 * ・・・・ 056 * { "カラム1":"値1" , "カラム2":"値2" , … } Nレコード目 057 * ] 058 * } 059 * 060 * writeTableParamタグで、key="LowerCase" value="true" を指定すると、 061 * すべてのカラムを小文字で出力します。(データ受信側の都合を配慮) 062 * 初期値は、false なので、基本的に大文字になります。 063 * 064 * @og.rev 5.6.0.3 (2012/01/24) 新規作成 065 * @og.group ファイル出力 066 * 067 * @version 4.0 068 * @author Kazuhiko Hasegawa 069 * @since JDK5.0, 070 */ 071public class TableWriter_JSON extends TableWriter_Default { 072 /** このプログラムのVERSION文字列を設定します。 {@value} */ 073 private static final String VERSION = "8.5.6.1 (2024/03/29)" ; 074 075 // 5.6.6.1 (2013/07/12) keys の整合性チェックを行います。 076 /** staticイニシャライザ後、読み取り専用にするので、ConcurrentHashMap を使用しません。 */ 077 private static final Map<String,String> KEYS_MAP = new LinkedHashMap<>(); 078 static { 079 KEYS_MAP.put( "JSONNAME" , "JSON形式で、配列をオブジェクトとしてまとめる場合に使う" ); 080 KEYS_MAP.put( "LOWERCASE" , "カラム名(=パラメータ名)を小文字にする場合、true をセット(初期値:false)" ); 081 } 082 083 private String jsonName ; // JSON形式で、配列をオブジェクトとしてまとめるパラメータを指定する場合に使う。 084 private boolean toLowerCase ; // カラム名(=パラメータ名)を小文字にする場合、true にセットします。初期値は大文字のまま。 085 086 /** 087 * デフォルトコンストラクター 088 * 089 * @og.rev 6.4.2.0 (2016/01/29) PMD refactoring. Each class should declare at least one constructor. 090 */ 091 public TableWriter_JSON() { super(); } // これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。 092 093 /** 094 * DBTableModel から データを作成して、PrintWriter に書き出します。 095 * 096 * @param writer PrintWriterオブジェクト 097 */ 098 @Override 099 public void writeDBTable( final PrintWriter writer ) { 100 super.setHeaderSequence( "D" ); 101 super.writeDBTable( writer ); 102 } 103 104 /** 105 * PrintWriter に DBTableModelのテーブル情報を書き込みます。 106 * 107 * 出力のXML形式は、ORACLE XDK での出力ファイルと同じ形式ですので、直接データベースに 108 * 登録することができます。 109 * 110 * @og.rev 5.6.0.3 (2012/01/24) JSON形式のフォーマットを作成します。 111 * @og.rev 5.6.1.2 (2013/02/22) jsonName もダブルクオートで囲う。 112 * @og.rev 6.0.1.2 (2014/08/08) カラム飛ばしできる機能を追加 113 * @og.rev 8.5.6.1 (2024/03/29) PMD 7.0.0 Finding duplicated code with CPD 114 * 115 * @param table DBTableModelオブジェクト 116 * @param writer PrintWriterオブジェクト 117 */ 118 @Override 119 protected void writeData( final DBTableModel table,final PrintWriter writer ) { 120 final int numberOfRows = table.getRowCount(); 121 122 final String[] names = table.getNames(); // 8.5.4.2 (2024/01/12) PMD 7.0.0 LocalVariableCouldBeFinal 123 if( toLowerCase ) { 124 for( int i=0; i<names.length; i++ ) { 125 names[i] = names[i].toLowerCase(Locale.JAPAN); 126 } 127 } 128 129 if( jsonName != null ) { 130 writer.println( "{" ); 131 writer.print( "\"" ); // 5.6.1.2 (2013/02/22) jsonName もダブルクオートで囲う。 132 writer.print( jsonName ); 133 writer.print( "\":" ); // 5.6.1.2 (2013/02/22) jsonName もダブルクオートで囲う。 134 } 135 136 writer.println( "[" ); 137 138// final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE ); // 6.1.0.0 (2014/12/26) refactoring 139 for( int row=0; row<numberOfRows; row++ ) { 140 if( row > 0 ) { writer.print( "," ); } // 最初以後の連結は、, を使う 141 writer.print( "{" ); 142 for( int i=0; i<numberOfColumns; i++ ) { 143 if( i > 0 ) { writer.print( "," ); } // 最初以後の連結は、, を使う 144 final int clm = clmNo[i]; 145 if( clm < 0 ) { continue; } // 6.0.1.2 (2014/08/08) カラム飛ばし 146 147 // 8.5.6.1 (2024/03/29) PMD 7.0.0 Finding duplicated code with CPD 148 // 注意:jsonFilter では、\(バックスラッシュ) と "(ダブルクオート) 以外もエスケープしています。 149 final String val = StringUtil.jsonFilter( table.getValue(row,clm) ); 150// String val = table.getValue(row,clm); 151// // \(バックスラッシュ) と "(ダブルクオート)のエスケープ 152// if( val.contains( "\\" ) || val.contains( "\"" ) ) { 153// buf.setLength(0); 154// for( int j=0; j<val.length(); j++ ) { 155// final char ch = val.charAt( j ); 156// if( ch == '\\' || ch == '\"' ) { buf.append( '\\' ); } 157// buf.append( ch ); 158// } 159// val = buf.toString(); 160// } 161 162 writer.print( '"' ); 163 writer.print( names[clm] ); 164 writer.print( "\":\"" ); 165 writer.print( val ); 166 writer.print( '"' ); 167 } 168 writer.println( "}" ); 169 } 170 171 writer.println( "]" ); 172 173 if( jsonName != null ) { 174 writer.println( "}" ); 175 } 176 } 177 178 /** 179 * パラメーターリストをセットします。 180 * JSONのパラメータ名を指定します。 181 * 引数が、null の場合は、何もしません。 182 * 183 * @og.rev 5.6.0.3 (2012/01/24) JSONのパラメータ名を指定します。 184 * @og.rev 5.6.6.1 (2013/07/12) keys の整合性チェックを行います。 185 * 186 * @param listParam HybsEntryリスト 187 */ 188 @Override 189 public void setParam( final List<HybsEntry> listParam ) { 190 if( listParam != null && ! listParam.isEmpty() ) { 191 for( final HybsEntry entry : listParam ) { 192 final String key = entry.getKey() ; // 5.6.6.1 (2013/07/12) keys の整合性チェック 193 checkParam( key , KEYS_MAP ); 194 195 final String val = entry.getValue() ; // 5.6.6.1 (2013/07/12) val を先に取得 196 if( val != null && val.length() > 0 ) { 197 // JSON形式で、配列をオブジェクトとしてまとめるパラメータを指定する場合に使う。 198 if( "JsonName".equalsIgnoreCase( key ) ) { 199 jsonName = val ; 200 } 201 // カラム名(=パラメータ名)を小文字にする場合、true にセットします。初期値はfalse:大文字のまま。 202 else if( "LowerCase".equalsIgnoreCase( key ) ) { 203 toLowerCase = Boolean.parseBoolean( val ); 204 } 205 } 206 } 207 } 208 } 209}