Field の getType は、Class

よく落ち着いてみれば、Java リフレクションの Field の getType の返却は、Class<?>
だから、以前書いた 入れ子構造のBean の中の Obejct を取得する(2) - Oboe吹きプログラマの黙示録
も、以下のとおり書き直せる。

import java.lang.reflect.Field;
import java.util.function.Function;
/**
 * UniqueFieldfinder
 */
@FunctionalInterface
public interface UniqueFieldfinder<T, R>{
   R find(T u);

   @SuppressWarnings("unchecked")
   static <T, R> Function<T, R> of(Class<R> c){
      return t->(R)search(c, t);
   }

   static <T> Object search(Class<T> c, Object o){
      Field[] fs = o.getClass().getDeclaredFields();
      String cname = c.getName();
      for(Field f:fs){
         String typename = f.getType().getName();
         if (typename.startsWith("[")) continue;
         try{
            f.setAccessible(true);
            if (f.getType().isPrimitive() || typename.startsWith("java.") || typename.startsWith("jdk.")
               || typename.startsWith("javax.")   || typename.startsWith("org.xml.")
               || typename.startsWith("org.w3c.")|| typename.startsWith("org.omg.") ){
               if (cname.equals(f.getType().getName())){
                  return f.get(o);
               }
               continue;
            }
            if (cname.equals(f.getType().getName())){
               return f.get(o);
            }
            Object v = f.get(o);
            if (v==null) continue;
            return search(c, v);
         }catch(IllegalArgumentException e){
         }catch(IllegalAccessException e){
         }
      }
      return null;
   }
}

でも、相変わらず、探索に指定する型が重複して持っている場合はうまく取得できない。
List や、Map、Collection は指定して取得できない。
配列も指定して取得できない。

という制約でこれはあまり使いものにならない。

依存関係のライブラリ(JAR)を含めた実行可能JAR を Gradle で作成する

Gradle の jar タスクは以下のようにする。

jar {
    excluse 'MET-INF/*.SF', 'MET-INF/*.DSA', 'MET-INF/*.RSA', 'MET-INF/*.MF', 
    manifest {
        attributes 'Main-Class': 'org.yips.HellowMain'
        'Class-Path': configurations.runtime.files.collect { "lib/%it.name" }.join(' ')
    }
    from configurations compile.collect { it.isDirectory() ? it : zipTree(it) }
}

Class-Path の書き方を上のようにする。

ついでに、作成した jarファイルを別のディレクトリにコピーするタスクは、以下のとおり。
(InteliJ の場合)

task distribute(type Copy){
   from 'build/libs'
   into 'dist'
   include '*.jar'
}

InteliJ でこの distribute タスクは Gradle タブで other の下に作られる。
f:id:posturan:20200206215737j:plain

JSON のソート(Javascript)

Python 使用時の JSON ソートを書いたので、次は JavaScript

oboe2uran.hatenablog.com

同様のJSONデータとする。

var data = {
  "title": "サンプル",
  "records":
   [
     {"item": "item 2", "point": 74.68, "date":"2020-02-01" , "memo":"う" },
     {"item": "item 1", "point": 4.63 , "date":"2020-02-02" , "memo":"あ" },
     {"item": "aitem 3", "point": 9.38,  "date":"2020-02-05" , "memo": null },
     {"item": "item 4", "point": 5.78,  "date":"2020-02-14" , "memo":"い" },
     {"item": "item 5", "point": 5.53,  "date":"2020-02-07" , "memo":"え" }
   ]
};

日付の昇順

const sorted_records = data['records'].sort((a, b)=> a.date > b.date ? 1 : -1 );

pointの降順

const sorted_records = data['records'].sort((a, b)=> a.date > b.date ? -1 : 1 );

memo のソート、
null がある場合、やはり、そのままではソートできないので、'' 空文字でソートさせる必要がある。

const sorted_records = data['records'].sort((a, b)=>{
   const c = a.memo==undefined ? "" : a.memo;
   const d = b.memo==undefined ? "" : b.memo;
   return c > d ? 1 : -1
});
data['records'] = sorted_records;

どうしても1行で済ませたいなら、括弧でくくる。

const sorted_records = data['records'].sort((a, b)=>(a.memo==undefined ? "" : a.memo) > (b.memo==undefined ? "" : b.memo) ? 1 : -1);

JSON のソート

Python での JSON のソートサンプル

data.json

{
  "title": "サンプル",
  "records":
   [
     {"item": "item 1", "point": 4.63 , "date":"2020-02-02" , "memo":"" },
     {"item": "item 2", "point": 74.68, "date":"2020-02-01" , "memo":"" },
     {"item": "item 3", "point": 9.38,  "date":"2020-02-05" , "memo":null },
     {"item": "item 4", "point": 5.78,  "date":"2020-02-14" , "memo":"" },
     {"item": "item 5", "point": 5.53,  "date":"2020-02-07" , "memo":"" }
   ]
}

sorted を使用してソートする。

# -*- coding: UTF-8 -*-
import json

with open('data.json', 'r', encoding='utf-8') as f:
    # JSONファイル→辞書
    data = json.load(f)
    data['records'] = sorted(data['records'], key=lambda k: k['point'], reverse=True)
    str = json.dumps(data, indent=4, skipkeys=True, ensure_ascii=False)
    print(str)

日付の昇順ソート

    data['records'] = sorted(data['records'], key=lambda k: k['date'], reverse=False)

memo のソート、
null がある場合、そのままではソートできないので、'' 空文字でソートさせる必要がある。

    data['records'] = sorted(data['records'], key=lambda k:  '' if k['memo']==None else k['memo'], reverse=False)

↑ memo のソートの結果は、以下のようになる。

{
    "title": "サンプル",
    "records": [
        {
            "item": "item 3",
            "point": 9.38,
            "date": "2020-02-05",
            "memo": null
        },
        {
            "item": "item 1",
            "point": 4.63,
            "date": "2020-02-02",
            "memo": ""
        },
        {
            "item": "item 4",
            "point": 5.78,
            "date": "2020-02-14",
            "memo": ""
        },
        {
            "item": "item 2",
            "point": 74.68,
            "date": "2020-02-01",
            "memo": ""
        },
        {
            "item": "item 5",
            "point": 5.53,
            "date": "2020-02-07",
            "memo": ""
        }
    ]
}

BATファイルへのドラッグ&ドロップ

なぜか、気がつかなった。。。
BATソース内、
変数名 %1 , %2 , %3 , .....

BATファイルアイコンに、Window で選択したファイルをドラッグ&ドロップすると
ドロップしたファイルパスが渡る。

以下のように、BATをを書いてみればわかる。

@echo off
set f1=%1
echo %f1%
pause

では、複数ファイルを選択したファイルをドラッグ&ドロップしたのは、渡せるのか?

@echo off

set f1=%1
set f2=%2
set f3=%3

echo %f1%
echo %f2%
echo %f3%

pause

渡せる!ただし、この選択した順番どおりに、%1, %2 , %3 と並ばなかった!

去年、作ったツールのBATファイルも、この方法で、BATを書けば良かったので修正。
Home · yipuran/daria Wiki · GitHub

JSONの整形

JavaScript , Python, Java(gson) それぞれのJSON整形

JSON 素材

{"a":1,"b":"ABC","c":[1,2,3],"d":{"A":"x", "e":{ "B":23, "C":45},"f":null}}

JavaScript
JSON.stringify で充分、詳細は、JSON.stringify() - JavaScript | MDN
をよく読むこと。

const data = {"a":1,"b":"ABC","c":[1,2,3],"d":{"A":"x", "e":{ "B":23, "C":45},"f":null}}
pretty = JSON.stringify(data, null, 4)

結果

{
    "a": 1,
    "b": "ABC",
    "c": [
        1,
        2,
        3
    ],
    "d": {
        "A": "x",
        "e": {
            "B": 23,
            "C": 45
        },
        "f": null
    }
}

replacer を指定する。

// 値が文字列なら出力させない置換関数
function replacer(key, value) {
  if (typeof value === 'string') {
    return undefined;
  }
  return value;
}
pretty = JSON.stringify(data, replacer, 4)
console.log(pretty)

結果

{
    "a": 1,
    "c": [
        1,
        2,
        3
    ],
    "d": {
        "e": {
            "B": 23,
            "C": 45
        },
        "f": null
    }
}

Python
json をインポートして、load で辞書として読み込む

# -*- coding: UTF-8 -*-
import json

with open('sample.json', 'r') as f:
    # JSONファイル→辞書
    dict = json.load(f)
    str = json.dumps(dict, indent=4)
    print(str)
    # 書込み
    with open('test.json', 'w') as fw:
        json.dump(dict, fw, indent=4)

Json Value に、日本語がある場合は、json.dump を以下のように、ensure_ascii=False を指定しないと \uxxxx と出力されてしまう。

       json.dump(dict, fw, indent=4, ensure_ascii=False)

Java(Gson)
GsonBuilder で、setPrettyPrinting() を指定して Gsonインスタンスを生成する。

Gson gson = new GsonBuilder().serializeNulls().setPrettyPrinting().create();

JsonReader reader	 = new JsonReader(new StringReader(str));
Map<String, Object> map = gson.fromJson(reader, new TypeToken<Map<String, Object>>(){}.getType());

System.out.println(gson.toJson(map));

Gson#toJson() が生成するJSON文字列は、setPrettyPrinting() を実行している場合、
インデントがデフォルトで、空白2文字固定になっている。
このインデントサイズが、気にいらなければ、以下のように、JsonWriter で、setIndent( インデント文字列 ) を指定するしかない。

StringWriter sw = new StringWriter();
JsonWriter jw = gson.newJsonWriter(sw);
jw.setIndent("    ");
gson.toJson(map, new TypeToken<Map<String, Object>>(){}.getType(), jw);
jw.flush();
jw.close();

System.out.println(sw.toString());