自由気ままにブログ

地方零細企業プログラマがひっそりとなにかしています。

DBから、jsonファイルの作成 ハマったところ

f:id:nono138:20200421225921p:plain

Goでデータベース(以下略:DB)の情報をブラウザ側に渡したい時に、jsonファイルで渡すことにしました。

DBの操作が出来れば、結構簡単に出来ると思います。

それでは、さっそくサンプルコードを見てみましょう。

DBのデータ取得

nono138.hatenablog.com

以前書いたもので、selectDB関数のところを参考にして頂ければ、DBから情報を取得出来ると思います。

 

JSONファイルに書き出す用の準備

上記のコードの変更点を注目していきたいと思います。

import (
    "database/sql"
    "encoding/json"
    "fmt"
    "time"

    _ "github.com/mattn/go-sqlite3"
)

//データベース用構造体
type Userinfo struct {
    Chord      string     `json:"chord"`
    UserName   string     `json:"userName"`
    Category   string     `json:"category"`
    RecordDate *time.Time `json:"recordDate"`
}

 

importに新たに"encoding/json"が追加されていますね。

こちらは、読んで字の如く、jsonファイルの形式にエンコード(変換)してくれます。

構造体の後ろに'json:"キー"'が追加されていますね。

キーがjsonでの変数名になります。

値(value)は構造体に入った値が入ります.

SELECTした値をjson形式にする

selectDB関数の変更点を見てみましょう。

//テーブルのデータを一括検索
func selectDB(DB *sql.DB) (err error) {
    //テーブルデータ取得
    cmd := "SELECT * FROM userinfo"
    rowserr := DB.Query(cmd)
    if err != nil {
        fmt.Println(err)
    }

    defer rows.Close()

    var userArray Userinfo
    for rows.Next() {
        var user Userinfo
        err := rows.Scan(&user.Chord, &user.UserName, &user.Category, &user.RecordDate)
        if err != nil {
            fmt.Println(err)
        }
        userArray = append(userArray, user)
    }
    for _user := range userArray {
        jsonBytes_ := json.Marshal(user)
        fmt.Println(string(jsonBytes))
        // fmt.Println(user.chord, user.userName, user.category, user.recordDate)

    }
    return nil
}

結果

{"chord":"1","userName":"テスト 太郎","category":"男性","recordDate":"1970-08-22T19:13:38Z"}

{"chord":"2","userName":"テスト 花子","category":"女性","recordDate":"1970-08-22T19:13:38Z"}

 

正しい、json形式になっていますね。

json, _ := json.Marshal(user)が追加されていますね。

これで、json形式に直してくれます。

ちなみに、jsonBytesの型はuint8 (uint8のスライス)なので、そのまま出力されると、byteで出されるので人間にはなんのこっちゃになるので、今回は分かりやすくstring型にキャストしてあります。

ブラウザ側に送る際には、http.ResponseWriterのWrite(jsonBytes)で送りましょう。

ブラウザ側でパースすると通信での負荷が多少は減ります。

 

今回は、構造体の配列で渡していますが、mapのinterfaceでも渡すことができます。

ハマったところ

構造体の設定のところで躓きました。

原因のソース部分

//データベース用構造体
type Userinfo struct {
    chord      string     `json:"chord"`
    userName   string     `json:"userName"`
    category   string     `json:"category"`
    recordDate *time.Time `json:"recordDate"`
}

結果

{}

{}

...はい。

 

さっきと何が違うの?と思うところはありますが、変数名が小文字になっています。

json.Marshalメソッドは、変数が大文字でないと上手く値を返してくれません

private関数はどうやら認識してくれないようです。

json.Marshalを使う時には、public関数で使いましょう。

どう言う思想の元で作られたのかは分かりませんが、個人的にあまり好きではない仕様ですね。

この仕様の理由は、公式Docをみると、名前の競合の解決の為となっています。

Embedding types introduces the problem of name conflicts but the rules to resolve them are simple. First, a field or method X hides any other item X in a more deeply nested part of the type. If log.Logger contained a field or method called Command, the Command field of Job would dominate it.

Second, if the same name appears at the same nesting level, it is usually an error; it would be erroneous to embed log.Logger if the Job struct contained another field or method called Logger. However, if the duplicate name is never mentioned in the program outside the type definition, it is OK. This qualification provides some protection against changes made to types embedded from outside; there is no problem if a field is added that conflicts with another field in another subtype if neither field is ever used.

 

なるほど、と言いたい所ですがなんだかなぁと思います。

嫌なら、自分で作れってことですね😂

せめて、エラーで吐いてくれたら納得出来るのですが、nilで返ってきたお陰で原因が分かるのに時間を取られました🤣

個人的書き置き場

あとは、null対応ですが、sqlite3が優秀なのか、mattnさんが優秀なのか、ぬるぽが出ないので、新たにDB構築するのは面倒臭いので、また気が向いた時に書こうかなと思います。

ソースは載せませんが、一応、interfaceで対応は出来ます。

ソースのレビューもこうした方が良いよなどのご意見も随時募集しているので、コメント・twitterなどに言って頂けると幸いです。