BEACHSIDE BLOG

Azure と GitHub と C# が好きなエンジニアの個人メモ ( ・ㅂ・)و ̑̑

.NET8 での Azure Functions の開発を始めるための準備 ( C# )

Azure Functions 開発入門として、.NET のバージョンが更新する度に Visula Studio 2022 での恒例の作業のなった Azure Functions のツールセットの更新の方法の話です。

Azure Functions に新しい .NET のバージョンが選べない?!

更新前の状態で Visual Studio 2022 を起動して Azure Functions のプロジェクトを作ろうとすると、.NET8 がない状態とします (去年スクショだけとっておいてよかった←ブログ書くの放置してた💦) 。

新しいバージョンないやん!と困ったときに、Azure Functions のツールセットの更新をすることで解決できる可能性は大です。

Azure Functions でツールセットの更新

Visual Studio 2022 が開いてない状態であれば "コードなしで続行" して起動し、上部のメニューで、"ツール" → "オプション" をクリックします。

"プロジェクトおよびソリューション" の中の "Azure Functions" → "更新の確認" → "ダウンロードしてインストール" をクリックします。画面上ダウンロードしてる雰囲気が出てないですが、 "ダウンロードしてインストール" のボタンが消えると更新完了です。

これで最新の状態なります。プロジェクトを作成すると、今回だと .NET 8 (Isolated) も表示されるようになりました (in-process ではない点注意!)。

isolated は,まだまだ完成度が高いとはいえないのでで、個人的には .NET 8 では in-process が来るのを待ちます。in-process については、公式だと以下のリンクで2024年はじめにはくる予定と書かれています。

.NET on Azure Functions – August 2023 roadmap update - Microsoft Community Hub

あとは、あまり頻度が高くないですが Azure Functions のプロジェクトのテンプレートの更新が来た時も使うので、.NET のバージョンだけでなくプロジェクト作成時のテンプレートが周りの人と違うなーとか古いなーってときにも更新してみるとよいかもしれませんね。

OpenAI: 文章のトークン数を数える (C#, Python, Node)

OpenAI / Azure OpenAI でとりあえずトークン数を数えることってちょいちょいありますよね。今回は C#, TypeScript のついでに Python もメモしておこうかなという話です。

はじめに: トークンとは

トークンとはなんじゃって改めて感じた場合は、 以下の OpenAI のドキュメントを読むと参考になります (なるかな...)。

と雑にふれたところで、さっそく各言語での実装を見ていきます。

Python: tiktoken

これは言うまでもなくtiktoken ですね。サンプルはこんな感じ。

import tiktoken

def count_token(text: str, model_name: str) -> int:
    encoding = tiktoken.encoding_for_model(model_name)
    token_count = len(encoding.encode(text))
    return token_count

count = count_token("こんにちは、猫さん", "gpt-3.5-turbo")
print(f"{count=}")


model 名から encoding のオブジェクトを作ることが多いですが、参考までに encoding 名からもできます。各 model の encoding 名はこんな感じ。

Encoding name OpenAI models
cl100k_base gpt-4, gpt-3.5-turbo, text-embedding-ada-002
p50k_base Codex models, text-davinci-002, text-davinci-003
r50k_base (or gpt2) GPT-3 models (davinci とか)

で、コード。

import tiktoken

def count_token_from_encoding_name(text: str, encoding_name: str) -> int:
  encoding = tiktoken.get_encoding(encoding_name)
  token_count = len(encoding.encode(text))
  return token_count

count = count_token_from_encoding_name("こんにちわ、猫さん", "cl100k_base")
print(f"{count=}")

余談ですが「こんにちは」じゃなくて間違った日本語である「こんにちわ」にするとトークンが増えますね。

C#

Tokenizer

Microsoft が OSS として公開している Tokenizer でトークン数を数えれます。tiktoken の実装をベースにしてるみたいですね。

NuGet で Microsoft.DeepDev.TokenizerLib をインストールしてあとはこんな感じ。実際にトークン数えてるのは26-32行目のメソッド。

`

補足をいくつか書いておきます。

  • 28行目の tokenizer をインスタンス化する TokenizerBuilder.CreateByModelNameAsync はちょっとコストがかかるのでモデルが決まりきっててトークン数を数える回数も多いなら singleton で保持しておいた方がよさそう。
  • 31行目は、トークンを数えるテキストに対して29行目でトークンを追加しているので、その分で2を引いています。
  • TokenizerBuilder.CreateByEncoderNameAsync メソッドを使って encoder name で tokenizer の初期化もできます。
  • model name や encoder name は実装から一覧をみて書いた方がよさそう。
    • Tokenizer/Tokenizer_C#/TokenizerLib/TokenizerBuilder.cs
    • これ書いてる時点だと、model 名が OpenAI の方の名称をベースにしている。そのため Azure AI Studio で表示される model name: gpt-35-turbo だとエラー、OpenAI での model name: gpt-3.5-turbo としないとあかんとか微妙な点があるため。

Semantic Kernel: GPT3Tokenizer

Semantic Kernel の中に GPT3Tokenizer ってのがあってこんな感じでサクッと使えます。

using Microsoft.SemanticKernel.Connectors.AI.OpenAI.Tokenizers;

const string sampleText = "";
var tokenCount = GPT3Tokenizer.Encode(sampleText);

実装を見るとまだこのひとつしかないみたい。これは p50k_base のエンコーダーで、で足りているので、text-davinci-003 や code-davinci-002 で使われているやつ。つまり、gpt-4, gpt-3.5-turbo や text-embedding-ada-002 で使われている cl100k_base とは異なる古いやつなので、積極的に使えるものでなない。ということで、Tokenizer 使いましょうって話になります。

TypeScript (JavaScript)

Tokenizer

TypeScript は、先述の C# と同じ repo で Microsoft が OSS として公開している Tokenizer でやれます。

GitHub Packages Registry を使ってるのでそこから package をダウンロードしつつ C# と似た実装で処理するって感じです。C# とあまり変わらないので参考リンクだけにしておきます (C# と Python は私が使ってるで書いたけど、TS は今のとこ使う予定無いのでメモだけでいいやと雑に扱いました)。

GPT-3-Encoder

GPT-3-Encoder もあるけど、最近更新されてないから Tokenizer 使った方が無難です。

www.npmjs.com

参考

github.com

GitHub Actions で ASP.NET MVC ( .NET Framework 4.8 ) を Azure App Service (Web App) へデプロイ

ASP.NET MVC ( .NET Framework 4.8 ) のアプリを GitHub Actions での CI/CD して Azure の App Service (Web Apps) へのデプロイするまでの方法を書いていきます。

ここら辺のドキュメントとか情報は少ないのが書こうと思ったモチベーションでした...と思って下書きして数か月放置してして今に至ります。

序盤はビギナー向けに GitHub の UI で初めて YAML を書くときの流れや Tips をダラダラ書いています。
ビギナーじゃない方には、後半のセクション: YAML のコード全体 もおいてるのでそれだけ見ればいい感もあります。

準備: ASP.NET Web アプリケーション (.NET Framework) のプロジェクト作成

今回は ASP.NET MVC (.NET Framework) のアプリの CI/CD の話なので、VS 2022 で ASP.NET Web アプリケーション (.NET Framework) を選びます。ここら辺は説明雑に進めます。

今回は MVC のテンプレートを選びましたが、Web API や空のやつでもシンプルに C# /Razor だけでコード書くなら一緒だと思います。SPA はビルド時に色々あるのかなとかだし Web Form は使う気がないので試す気はないので、この二つは対象外。

プロジェクトができてフォルダーを見たら .gitignore のファイルが無かったので、.sln ファイルがあるフォルダで以下のコマンドで作成しました。

dotnet new gitignore

ちなみにルートのフォルダはこんな感じ。ルートに sln ファイルがあります。csproj のパスは .\AspnetMvc48\AspnetMvc48.csproj です。

あとは GitHub 上でやろうと思うので、GitHub の repo に push しちゃいます (ローカル環境で YAML 書いても別に一緒ですが) 。

GitHub Actions の作成

今回は入門向けに GitHub Actions の UI 上で workflow を作る方法で進めます。

慣れてくると GitHub Actions で実際に使う Action が公開されている repo の README を確認しながらやった方が早いかもです。

YAML ファイルの追加

GitHub でプロジェクトを置いた repo を開き Actions タブをクリックします。GitHub Actions の workflow が存在しない場合はこんな画面になるので、set up a workflow yourself をクリックします。

既になんらかの workflow がある場合は New workflow ってボタンをクリックすると上の画面に行きます。

これで、ファイルができるので、まずは画面上部にファイル名が入力できます。わかりやすい名前をつけてあげましょう。ここでは aspnet-netframework-cicd.yml としました。

あとは name と以下のコードを書きます。

name: ASP.NET MVC (.NET Framework 4.8) to App Service

on:
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]
  workflow_dispatch:

jobs:
  build:
    runs-on: windows-latest

    steps:
      - uses: actions/checkout@v3

先頭の name は Actions タブを開いた際にこんな感じで見えます。後ろの文字までは見えないので、その点を考慮して UI 上で判別しやすい名前を付けてあげましょう。

あとは on とか最初の actions/checkout は毎度のことなので説明は省きます。バージョンはちょいちょいアップデートがあるのでドキュメント見ながら進めましょう。

msbuild のセットアップ

.NET Framework のビルドには MSBuild を使うのでそれを入れます。使うアクションは以下です。

何使えばわからないときの探し方として、まずは右側の Marketplace の検索で「msbuild」と入れればなんか出てくることがほとんどなので、検索しましょう。

microsoft が作った setup-msbuild がでるのでクリックするとこんな画面になります。Version を選んでコピーボタンクリックして貼り付けてながらコメントみるのもよいですし、View full Marketplace listing をクリックすると使い方の説明が書いてあることが多いのでそこのコードをはるのもよいです。

今回はシンプルにこれだけを追加します。

      - name: Add msbuild to PATH
        uses: microsoft/setup-msbuild@v1.1

NuGet のセットアップと NuGet パッケージの復元

NuGet をセットアップして Restore する

まずは NuGet の復元を NuGet CLI で行う方法を書きます。前述同様に検索したりして見つけて使いましょう。

setup と restore で追加するコードはこちら。今回はルートに AspnetMvc48.sln がある前提で nuget restore をしていますが、そうでない場合はただしいパスを指定しましょう。

      - name: Setup NuGet
        uses: nuget/setup-nuget@v1

      - name: Restore NuGet packages
        run: nuget restore AspnetMvc48.sln

msbuild のオプションで復元も可能

この方法は諸条件が必要だったりするのでドキュメントのリンクだけにします。

ビルド

msbuild の実行

後は msbuild コマンドでビルドを実行するだけです。

今回は .sln ファイルのパスは .\AspnetMvc48.sln なのでそのファイルを指定します。

     - name: Build
        run: msbuild AspnetMvc48.sln /p:Configuration=Release /p:DeployOnBuild=true /p:DeployDefaultTarget=WebPublish /p:WebPublishMethod=FileSystem /p:publishUrl=${{ github.workspace }}\publish /p:PrecompileBeforePublish=true /p:EnableUpdateable=false

色々とオプションがついていますが、これは Azure の App Service (Web App) に最適なデプロイオプションだと思ってください。詳しい話はしばやんのブログが一番わかりやすいです。公式ドキュメントでは探しにくいです。

Azure Pipelines から Run From Package としてデプロイする - しばやん雑記

その他参考になるドキュメントとして、直接的なプロパティのドキュメントではないし .NET Core のドキュメントなので msbuild の話から多少それるんですが、以下のドキュメント書かれてるプロパティの項目・使い方は参考にしてます。

ASP.NET Core アプリを配置するための Visual Studio 発行プロファイル (.pubxml) | Microsoft Learn

より実践的な話として、このジョブ内で単体テストや .editorconfig を使ったフォーマットのチェックなど、p実行したいところですが、本質からはずれるのでここでは書いてません。

ポイント1 : プリコンパイルのオプション

ASPNET MVC の プリコンパイル をするオプションである /p:PrecompileBeforePublish=true は、プロジェクトの構成によっては仕様上エラーでできないこともあるので、エラーになる際は外しましょう。

ポイント 2: ビルド成果物 (build artifact) の出力パスの指定

ここでは build artidact は /p:publishUrl=${{ github.workspace }}\publish と指定しました。GitHub Actions の github context を使っています。

相対パスで書いても全然 OK です。
ただ、より複雑なワークフローを作ってると workind-directory を使ったりコマンド内でパスを変更することがあると出力のパスが変わって後続の処理で影響が出るケースもことがある `${{ github.workspace }}`` を明示的に書いています。

(と、私がむかーしはまったので、個人的にはこういう策をとっています)

ポイント 3: GitHub Actions の msbuild の実行ログ

GitHub Actions で msbuild 実行時にログに大量に出てよくわからんとかログをもっと出したいとかは、ログのレベルを変えることで見やすくできます。以下ドキュメントリンクで -verbosity:level と書かれている部分に設定できるオプションの記載があります。省略形だと -v:m とか -v:diag とかで指定します。エラーの解析時にあまりにエラーが多いときは m (minimal) だと比較的エラーのみをすっきり出してくれます。

ポイント 4: コマンドの実行はまずローカルで試そう

msbuild のコマンドのエラーがでると GitHub Actions 上で調査は生産性が低いので、まずは実行したいコマンドをローカル PC で実行して動くことを試しましょう。その際、先述の github コンテキストを使ったパスの指定は自分の PCのパスや相対パスに直す必要があります。

Artifact のアップロード

GitHub Actions では、ジョブ間でファイルを共有する場合、専用のストアへアップロードする必要があります。
ジョブ自体は基本的に個別に動いているため、特定のジョブでファイルを GitHub Actions のホストに置いたからといって別のジョブではそのファイルは存在しないのです。

アップロードは actions/upload-artifact を使うだけで簡単です。

name は適当ですが、アップロードするファイルのパスはビルド時に指定した場所になります。これでこのジョブは完了です。

      - name: Upload Artifact
        uses: actions/upload-artifact@v3
        with:
          name: app
          path: ${{ github.workspace }}\app.zip

Artifact のデプロイと App Service へのデプロイ

前のジョブでアップロードした build artifact をダウンロードするのは actions/download-artifact を使ってサクッとできます。

App Service へのデプロイは azure/webapps-deploy を使っているだけなのでここでは詳しく書きませんが azure/webapps-deploy の README みれば。

 deploy:
    needs: build
    runs-on: windows-latest
    steps:
      - name: Download app
        uses: actions/download-artifact@v3
        with:
          name: app
          
      - name: Deploy to App Service
        uses: azure/webapps-deploy@v2
        with: 
          app-name: "app-eru-netframework-mvc"
          publish-profile: ${{ secrets.AZURE_APP_SERVICE_PUBLISH_PROFILE }}
          package: ${{ github.workspace }}\app.zip

補足な余談として、Azure へのログインはこれでやるのが現時点のベストな方法です。

blog.beachside.dev

YAML のコード全体

作るための手順を長々と書きましたが、できたものはこちら。

利用している action のバージョンはこのブログ書いてる時点の最新版なので、未来でこれを参考にする際は最新のバージョンを確認しながら構成していきましょうね。

まとめ

.NET Core にしたらもうちょっとしゅっとできて平和になります。

GitHub Actions で Azure API Management の CI/CD (ASP.NET Core 6)

Azure API Management の裏に Web API があると、Web API の CI/CD と一緒に API Management の APIs も更新したいですよね。

公式ドキュメント ではめんどくさそうな実現方法が書かれていますが、APIs だけ更新したいなら Azure CLI で実現するのがシンプルでよいと思っています。

今回は ASP.NET Core 6 の Web API でやりますが Open API のファイルを出力できるならどれでも一緒です。GitHub Actions でのざっくりな流れは以下です。

  • Web API をビルド時に Open API (Swagger) の定義ファイルを出力する
  • Web API をデプロイする
  • Open API (Swagger) の定義ファイルをもとに API Management の APIs を更新する

az apim api import を使うときに知っておきたいこと

Azure API Management の APIs の更新は Azure CLI でやります。

使うコマンドのドキュメントは以下です。ドキュメント通りにコマンドを実行すればよいだけに見えて説明が薄く闇深いので、気になる点を書いていきます。

az apim api import (az apim api) | Microsoft Learn

az apim api import の使い方と闇

APIM の APIs を更新したい場合は、先述のドキュメントで書かれている必須のパラメーター4つの他に、最低限必要なものがあります。

※ ちなみにこれは2022年10月現在の状況なのでバグだったら直るかもしれません。

  • --api-id : 名称通り APIs のユニークな ID です。指定しないと毎回ランダムな値がセットされてしまいます 。つまり別の APIs ができることになる。そして既存の APIs の"path" が重複していると怒られる。同じ APIs を更新する際は事実上必須です。
  • --display-name: セットしないと更新時に "MY API" に書き換えられるため事実上必須と言ってよいでしょう (これはバグな気がする) 。
  • --service-url: セットしないと更新時に空になるので事実上必須です (これはバグな気がする) 。

それぞれのパラメーターが Azure Portal の APIM のリソース (APIs → 対象の API をクリック → Settings タブ) でどれにあたるかを補足しておくとこんな感じです。

パラメーター Azure Portal での値
--display-name Display name (①)
--api-id Name (②)
--service-url Web service URL (③)
--path API URL suffix (④)
--service-name API Management のリソース名

他のオプショナルなパラメーターは必要に応じてつける感じです。

ローカルでコマンドを実行するサンプル

前置きが長くて集中力が低めの私ですが、先述の内容を加味してコマンドのフォーマットは以下です。

az apim api import --resource-group <RESOURCE GROUP> --service-name <APIM SERVICE NAME> --display-name <DISPLAY NAME> --api-id <NAME> --service-url <SERVICE URL> --path <API URL SUFFIX> --specification-format OpenApiJson --specification-path <FILE PATH>

実行の例はこんな感じ。

前提知識: GitHub Actions 実行前に知ってきたい基礎

もう GitHub Actions で動かしたいお気持ちですが、最低限 ? 知っておかなあかん前提知識を2つあげます。

1つ目: CI 時に ASP.NET Core 6 で OpenAPI の定義ファイルを出力

これを書くと長くなるので前々回のブログにまとめました。

blog.beachside.dev

2つ目: GitHub Actions での Azure へのログインは OIDC

昨年 OIDC がサポートされてからは、基本的に OIDC でのログインがベストプラクティスです。ということでここも一緒に書くと長くなりそうだったので前回のブログに書きました。

blog.beachside.dev

GitHub Actions での実行

事前に以下の2つがやってある前提で話を進めます。

  • "前提知識" の "2つ目: GitHub Actions で Azure へのログイン" で示した OIDC の設定。
  • デプロイ先の API Management と Web App のリソースの作成が作成。

YAML のサンプル

コアな部分は先述で書いているので説明は省きますが、シンプルに組み合わせた基本的なコードです。

※ 14 - 25行目の env 定義は私の環境のものなので各々で試すときは変更が必要です。

この YAML での注意点

シンプルなサンプルなので、プロダクションで利用するにあたり考慮してない点をいくつか書いておきます。

  • 本質からちょっと脱線する Web API は build 時に zip した方が速くね?とか Service URL は動的にとってきた方がいんじゃねとかそういう最適化も行ってないです。Web App のデプロイ時に Blue/Green デプロイするとか、要所で Environment 使って承認をさせるとかはもちろん触れてません。
  • APIM の APIs を作成するときの subscription とかの考慮もしてないです。

参考

enum に任意の文字列を割り当てる (C#)

過去に何度もやってると思うけど調べる機会があったので自分でメモしておこうと思います。

ここではパフォーマンスを気にしないケース (ざっくりなイメージですが 1000 回実行で 3~7 ミリ秒くらいが許容されるくらい) を想定しています。一般的なエンタープライズ系のアプリなら問題ないと思いますが、ゲームとかでハイパフォーマンスが必要なときは別の手段を使いましょう。

Attribute を使った文字列の変換のサンプルをメモしておきます。

実装サンプル

例えばこんな enum があったとして。

public enum Pets
{
    Dog,
    Cat,
    Otters,
    Rabbit
}

ここに DisplayAttribute (System.ComponentModel.DataAnnotations namespace) を使って Name を定義してそれを出力できるようにします。

public enum Pets
{
    [Display(Name = "いぬ")]
    Dog,
    [Display(Name = "にゃん")]
    Cat,
    [Display(Name = "かわうそ")]
    Otters,
    [Display(Name = "ぴょんぴょん")]
    Rabbit
}

ちなみに DisplayAttribute を使わなくても他の Attribute を使ったり、自作の Attribute を作ってもよいかと思いますがカスタムするほどでもないかなと。

あえて DisplayAttribute を使う理由があるとしたら、表示するための色々な機能が組み込まれているので、他のユースケースがあったときにさくっと使える点でしょうか。それ以上のユースケースがあれば必要に応じて自作の Attribute を作ればいいかなくらいに思ってます。

docs.microsoft.com

あとは拡張メソッドで DisplayAttribute をとれるようにします。ここでは DisplayAttribute がついてない場合、enum の文字列をそのまま返すようにしてますが、 DisplayAttribute が無い場合に例外をはくとか null を返すとかはユースケース次第でしょう。

public static class PetsExtensions
{
    public static string ToDisplayName(this Pets source)
    {
        string internalFormat = source.ToString();
        var displayAttribute = source.GetType().GetField(internalFormat)?.GetCustomAttribute<DisplayAttribute>();
        return displayAttribute?.Name ?? internalFormat;
    }
}

使うときはこんな感じですね。

// ぴょんぴょん
var rabbitDisplayName = Pets.Rabbit.ToDisplayName();

参考

docs.microsoft.com

GetCustomAttribute(ParameterInfo)

複数プロジェクトあるソリューションの C# の Language Version (言語バージョン)をいい感じに統一する

C# でひとつのソリューションの中に複数のプロジェクトがあるのはよくあると思います。そしてTarget framework のデフォルトの言語のバージョンを上位バージョンにあげることってよくありますよね。

今回は「Directory.Build.props で指定することでソリューション内のプロジェクトを一括で管理できてといいぞ」って話です。csproj で指定する方法も合わせて書いておきます。

予備知識

今回は、.NET Core 3.x のプロジェクト (言語バージョンのデフォルトは 8.0) を 9.0 にあげる例で話を進めます。ほかのフレームワークのデフォルトバージョンや、指定できるバージョンの文字列の一覧は以下のドキュメントに書いてます。

csproj でバージョンを指定する (微妙 💀)

各プロジェクトの .csproj ファイルを開いて、以下のように <LangVersion>9.0</LangVersion> を追加してあげることができます。

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp3.1</TargetFramework>
    <LangVersion>9.0</LangVersion>
  </PropertyGroup>
</Project>

これだと、単一のプロジェクトならいいけど、プロジェクトがたーーーーくさんあると管理がめんどくさくて微妙なのでおすすめできません。

Directory.Build.props でバージョンを指定する (Good 👍)

まず、ソリューションの直下に "Directory.Build.props" というファイルを追加します。
VS 2019 でファイルを追加したあと、ソリューションエクスプローラーからは "Solution Items" の中に入っているように見えますがファイルが直下にあれば問題ありません。

f:id:beachside:20210820014526p:plain

あとは Directory.Build.props の中身を以下のようにします。既存でファイルが存在してる場合はその構造を破壊しないように良しなに LangVersion を追加しましょう。Directory.Build.props はソリューション全体に影響するファイルなので、おかしなことをすると全てのプロジェクトを死に至らせる破壊力はあります。

<Project>
  <PropertyGroup>
    <LangVersion>9.0</LangVersion>
  </PropertyGroup>
</Project>

これで、例えば .NET Core 3.1 のコンソールアプリでも C# 9.0 の構文で以下図のように書けるのですが、画面からエラーが出たままの場合があります。その場合は Visual Studio の表示がおかしいだけなので、VS を再起動しましょう。正しい設定ができていればエラーは消えます。

f:id:beachside:20210820020028p:plain

余談ですが、Directory.Build.props で LangVersion を指定した後、csproj でも LangVersion を指定してみたら csproj の方で読み込まれました。

まとめ

"Directory.Build.props" はほかにも色々設定ができますが、そもそも Directory.Build.props ってなんぞやとか一緒にでてくる "Directory.Build.targets" については以下のドキュメントを読むとよいと思います。


そいえばまだ一度も VS 2022 使ってなくて VS 2019 のままなんだよなーそろそろ変えようかしらと思った今日この頃でした。

C# で System.Text.Json 使って Json を操作するときに気になったポイント( .NET Core 3 ~)

C# では .NET Core 3系にて Json のシリアライザーとして System.Text.Json が生まれました。
長らくお世話になった Json.NET - Newtonsoft とのお別れです。

多少お作法が異なるので、個人的に気になった点を整理してみました。

TL;DR

  • シリアライズする Unicode の範囲がデフォルトでは狭いので、日本語とか使うなら必要に応じて設定する
  • JsonSerializerOptions で色々設定できるのでドキュメントをチェック
  • 派生クラスの取り扱い注意

まとめると、とりあえずドキュメントは読みましょうって話です。

基本的な使い方

準備

NuGet で System.Text.Json をインストールすれば利用できます。

シリアライズとデシリアライズ

単純な利用方法としては、こんな感じです。

基本的に Json.NET と同じ空気感で利用できます。(空気感とは...)

日本語を使うならエンコードの設定が必要

上記のコードで名前に日本語で「横浜」入れてみると、エンコードされません。

\u6A2A\u6D5C と表示されてしまいます。文字コードのままですね。

これは、シリアライズする Unicode の範囲がデフォルトでは狭いからです。

下のコードの 6-10行目 にあるように UnicodeRanges を指定してあげれば日本語も問題なく利用できます。Serializer の各種オプションの指定には、JsonSerializerOptions というクラスを使います。これも Json.NET と似たような感じですね。

UnicodeRanges についてもう少し触れると、8行目でエンコーダーを All に指定しました。Unicode のコードの範囲を細かく指定することが可能で、その範囲はこちらに乗ってます。これ、細かく指定すべきなのかめんどいという意味で悩ましいです。

ついでに 9行目でなんとなくシリアライズする際にインデントがつくように設定してみました。

このコードを実行すると、以下図のようにインデント付きで日本語も正しく表示されます。

JsonSerializerOptions

まずはプロパティ名をキャメルケースにするのはほぼ使うことでしょう。想像だけで書けそうなやつですね。

var options = new JsonSerializerOptions()
{
      PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};

前述でエンコーダーを指定したりインデントをつけたりと使った JsonSerializerOptions ですが、以下のリンクに記載の JsonSerializerOptions のプロパティで何が設定できるか確認できます。

個人的には、

  • IgnoreNullValues: シリアライズ時に値が Null の場合はプロパティを無視する (Default: false)
  • IgnoreReadOnlyProperties: Rシリアライズ時に readonly のプロパティを無視する (Default: false)

とかはよく使うので意識してるところです。
ってか Json.NET では Reaonly property の無視って自分で Resolver 書いてた記憶があるけど...設定できたのかな...まぁいいや。

以下のリンクは、「シリアル化の動作を制御する」ってセクションの一部ですが、同じ並びに色んなパターンがあるのやりたいことはだいたいカバーできると思います。

派生クラスでの動作

こんなクラス(以下コード)があったとしましょう。そして似たのがたくさんあると毎回 Serialize のコード書くのめんどいし、それ己の振る舞いでしょってケースであれば、親クラス作って ToJson メソッドを作って Serialize するようにしますよね。
Json.NET 時代はこれで派生したクラス、つまり Person クラスのプロパティが Serialize できましたが、System.Text.Json だと親クラスのプロパティしか取れません。

以下図のように親のプロパティである Id しか出力されません。

これに関しては、以下でデフォルトではできないと記載がありました。あっそーかー残念って感想です。

ドキュメントに書いてある通り Serialize メソッドの Generics で object を指定してあげればいいって話ではあるのですが。ちょっぴり気持ち悪さを感じる気がします...。

    public class AnimalBase
    {
        public string Id { get; set; }

        public string ToJson() => JsonSerializer.Serialize<object>(this);
    }

参考

他にもドキュメントはとりあえず読んでおくべきところですが、その中でもいくつかピックアップしておきます。

Azure Functions の バインディング - Binder 編

Azure Functions では、関数の出力するデータをBlob や Queue に投げる処理を簡単にプログラムで書くことができます。
実装方法として、

があります。

今回は、Binder を使った命令型のバインディングの話です。

続きを読む

AWS Lambda から CloudWatch Events を呼ぶ

AWS Lambda から CloudWatch を呼ぶ際の実装メモです。

(2017/10月時点 =.NET Core 1.0しかサポートしてない時点の話です)

Overview

 1 開発環境の準備 (その1)
 2 .NET Core 1.0 対応の .NET Standard 1.6 のクラスライブラリの作成(その1)
 3 簡易なクラスライブラリー実装(その1)
 4 Console プロジェクトの作成(その2)
 5 Autofac の実装(その2)
 6 AWS Labmda プロジェクトの作成(その2)
 7 AWS Labmda の環境変数を読み込む
 8 API Gateway から Lambda - プロキシ統合 の使用とか
 9 AWS Lambda から AWS Lambda の呼び出し
 10 AWS Lambda から CloudWatch を呼ぶ(←今回ココ)

続きを読む