Json.NETを使って任意のクラスやDictionary・Listにデシリアライズする

Json.NETを使って、JSONを任意のクラスとDictionary・Listにそれぞれデシリアライズしてみます。

www.nuget.org

任意のクラス(POCO)にデシリアライズする

例えばアプリケーションの設定ファイルのように静的な構造のJSONであれば、専用のPOCOにデシリアライズするほうが扱いやすいです。

{
  "Server": {
    "DomainName": "domain.name",
    "WebServerHostNames": [
      "web1.host.name",
      "web2.host.name"
    ],
    "DbServerHostNames": [
      "db1.host.name",
      "db2.host.name"
    ]
  },
  "Table": {
    "AccountTableName": "account-table" 
  }  
}

JSONのキー名とPOCOのプロパティ名が一致していれば、JsonConvert.DeserializeObject<Config>で上のJSONファイルをConfigクラスにデシリアライズできます。
ConfigとServerConfigのように、クラスは入れ子になっていてもOKです。

using Newtonsoft.Json;

public class Config
{
    public ServerConfig Server { get; set; }
    public TableConfig Table { get; set; }

    public class ServerConfig
    {
        public string DomainName { get; set; }
        public IList<string> WebServerHostNames { get; set; }
        public IList<string> DbServerHostNames { get; set; }
    }

    public class TableConfig
    {
        public string AccountTableName { get; set; }
    }
}

var config = JsonConvert.DeserializeObject<Config>(File.ReadAllText("config.json"));

DictionaryとListへデシリアライズする

例えば以下のマスタデータのようにキー("Kanto"や"Kinki")が動的に増減するJSONの場合は、DictionaryやListとして扱える方がソースコードに手をいれる必要が無いので都合が良いです。

{
  "Area": {
    "Kanto": [
      "Tokyo",
      "Chiba",
      "Kanagawa"
    ],
    "Kinki": [
      "Osaka",
      "Kyoto",
      "Nara"
    ] 
  }
}

POCOと同じく、JsonConvert.DeserializeObject<>でDictionaryやListへデシリアライズできます。
この場合も入れ子になっていても良く、Dictionary<string, Dictionary<string, List<string>>>でもOKです。

using Newtonsoft.Json;

var area =
    JsonConvert.DeserializeObject<Dictionary<string, Dictionary<string, List<string>>>>(
        File.ReadAllText("area.json"))["Area"];

foreach (var key in area.Keys)
{
    Console.WriteLine(key);
    foreach (var value in area[key])
    {
        Console.WriteLine(value);
    }
}

Go+Gin+DynamoDBでWeb APIサーバを作る

仕事でGoを使うこととなりましたので、年明けから勉強しておりました。
一つの区切りとしてナンチャッテToDo Web APIサーバを作りましたので、GitHubで公開しておきます。

github.com

参考文献

Goは↓の本を読んでました。
他の言語と比較しながらGoの考え方やクセを細かく説明してくれるので、CやJavaの経験者であれば効率的に吸収できるかと思います。
Amazon CAPTCHA

GinはREADME、DynamoDBはAWSのドキュメントとQiita投稿で使い方を学びました。

github.com

dynamodb - Amazon Web Services - Go SDK

あえて aws-sdk-go で dynamoDB を使うときの基本操作 - Qiita

開発環境

Visual Studio Code(Windows)で構築しました。
↓の記事でコーディングからデバッグまでの一式できるようになるかと思います。
WindowsのVisual Studio CodeでGo言語の開発環境を作る - 素敵なおひげですね

構成

ユーザ登録、ログイン/ログアウト、ToDoの投稿と取得ができる簡単なWeb APIです。
Web APIフレームワークとしてGin、DBはDynamoDBを使っています。

Gin

Ginは高速さを売りとしているGoのWeb APIフレームワークです。
↓をビルド・実行するだけで、さくっとWeb APIサーバになります。

package main

import "gopkg.in/gin-gonic/gin.v1"

func main() {
    r := gin.Default()
    r.GET("/hello", func(c *gin.Context) {
        c.JSON(200, gin.H{
            "message": "Hello, World!",
        })
    })
    r.Run()
}

Goはnet/httpというHTTPサーバを標準搭載していますが、GinはAPIに特化していますのでそこは作りやすいようになっています。

  • 例えば、net/httpだと、リクエストbodyを文字列として読み込んでからデシリアライズ(アンマーシャル)する必要がありますが、
  • Ginでは、c.BindJSON(&request)だけでrequestへ値が詰められます。

DynamoDB

このAPIでは2つのDynamoDBテーブルにアクセスします。
テーブル名(とリージョン)はconfig.jsonで変更できます。

テーブル名 パーティションキー ソートキー
todo-user-table Id -
todo-todo-table Id UserId

GoからのDynamoDBのアクセスには素のaws-sdk-goを使っています。 なかなかツライです。

  • ソースコードを見ていただければわかるかと思いますが、DynamoDBへのCRUDでかなりの行数となっている上、ポインタとおまじないチックな手順が入り乱れています。
    • ExpressionAttributeNamesで項目名のプレースホルダ、ExpressionAttributeValuesで値のプレースホルダを指定したり、
    • map[string]*dynamodb.AttributeValue{ ":value": { S: aws.String(value),},},とかしんどいです。
  • 何らかのライブラリでラッピングしたくなる。

宿題

  • デプロイ
    • ローカルでサーバを実行し、DynamoDBのみAWSへアクセスしていました。
    • AWS ElasticBeanstalkにコマンドからデプロイできるようしたいところです。
  • テスト
    • 標準で揃っていますが、まだ手を出せてないです。

↓の本を読んで勉強します。
みんなのGo言語【現場で使える実践テクニック】 | 松木雅幸, mattn, 藤原俊一郎, 中島大一, 牧 大輔, 鈴木健太, 稲葉貴洋 |本 | 通販 | Amazon

Lambda(C#)の環境変数にアクセスする

今回はLambdaの環境変数C#からアクセスしてみます。 接続するDBを環境毎に切り替える場合などに役立ちます。

AWS Lambda の新機能 – 環境変数とサーバーレスアプリケーションモデル (SAM) | Amazon Web Services ブログ (結構最近追加された機能だったのですね。)

Lambdaの実装

まずはLambdaの実装です。
といってもこれまでのC#プログラミングと同じで、以下のようにEnvironment.GetEnvironmentVariableメソッドでアクセスできます。

// 環境変数"Path"にアクセスする場合
var pathValue = Environment.GetEnvironmentVariable("Path");

今回は"ENV_NAME"という名前で設定された環境変数にコンストラクタからアクセスしています。

using System;
using System.Collections.Generic;
using System.Net;
using Amazon.Lambda.Core;
using Amazon.Lambda.APIGatewayEvents;

[assembly: LambdaSerializerAttribute(typeof(Amazon.Lambda.Serialization.Json.JsonSerializer))]

namespace AWSServerless
{
    public class Functions
    {
        private readonly string envName;
        
        public Functions()
        {
            envName = Environment.GetEnvironmentVariable("ENV_NAME");
        }
        
        public APIGatewayProxyResponse Get(APIGatewayProxyRequest request, ILambdaContext context)
        {
            var response = new APIGatewayProxyResponse
            {
                StatusCode = (int)HttpStatusCode.OK,
                Body = $"This environment is {envName}",
                Headers = new Dictionary<string, string> { { "Content-Type", "text/plain" } }
            };

            return response;
        }
    }
}

Serverlessでデプロイ

AWSコンソールからでも設定可能ですが、前回同様Serverlessでデプロイしてみます。

ohke.hateblo.jp

serverless.templateのResources.Get.Properties.Environment.Variablesで、環境変数"ENV_NAME"に"test"という値で定義しています。

{
  "AWSTemplateFormatVersion" : "2010-09-09",
  "Transform" : "AWS::Serverless-2016-10-31",
  "Description" : "An AWS Serverless Application.",
  "Resources" : {
    "Get" : {
      "Type" : "AWS::Serverless::Function",
      "Properties": {
        "Handler": "AWSServerless::AWSServerless.Functions::Get",
        "Runtime": "dotnetcore1.0",
        "CodeUri": "",
        "MemorySize": 256,
        "Timeout": 30,
        "Role": null,
        "Policies": [ "AWSLambdaBasicExecutionRole" ],
        "Events": {
          "PutResource": {
            "Type": "Api",
            "Properties": {
              "Path": "/",
              "Method": "GET"
            }
          }
        },
        "Environment": {
          "Variables": {
            "ENV_NAME": "test"
          }
        }
      }
    }
  },
  "Outputs" : {
  }
}

これでAWS ExplorerからPublishして、AWSコンソールからデプロイしたLambdaを見ると環境変数が設定されていることを確認できます。 (Serverlessを使わない場合はこの画面から設定します。)

f:id:ohke:20170127223433j:plain

リリースしたURLにアクセスしてみると、"test"を取得できていることも確認できます。

f:id:ohke:20170127223539j:plain