1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

EDINETのXBRLを詩的に読む──Manpuku.Edinet.Xbrlの設計と思索

Posted at

🧭 はじめに:なぜこのライブラリを作ったのか

EDINETが提供するXBRLデータは、構造が複雑で扱いづらい。
PythonにはいくつかのXBRLライブラリがあるが、C#/.NET向けには決定版と呼べるものがなかった。

そこで私は、構文の美しさと保守性を両立するXBRLパーサを目指して、
このライブラリを設計・実装した。


📦 Manpuku.Edinet.Xbrlとは

主な機能

  • DTS(Discoverable Taxonomy Set)の解析
  • InlineXBRLのスキーマ・リンクベース解析
  • 拡張リンクロールの解決
  • 明示的な例外処理と最小限の外部依存

🚀 インストールと基本的な使い方

NuGetでインストール

dotnet add package Manpuku.Edinet.Xbrl

DI設定(例)

services.AddSingleton<IXbrlParser, XbrlParser>();

パースの基本

var parser = serviceProvider.GetRequiredService<IXbrlParser>();

// loader: URIからXDocumentを取得する非同期関数(呼び出し側で定義)
Func<Uri, Task<XDocument>> loader = async uri =>
{
    using var httpClient = new HttpClient();
    var stream = await httpClient.GetStreamAsync(uri);
    return XDocument.Load(stream);
};

var dts = await parser.ParseAsync(new Uri("https://example.com/entrypoint.xbrl"), loader);

🧪 サンプル:ファクトとコンテキストの出力

ここからは、EDINETのサンプルインスタンス(XBRLデータ)を使って、ファクト(記載された値)とそのコンテキスト情報を出力するプログラムを一緒に作っていきましょう。

今回は、C#のトップレベルステートメントを使ったシンプルなコマンドラインアプリとして構築します。

構文の流れを追いながら、XBRLの世界を少しずつ読み解いていきます。

🔧 まずは using 宣言から
最初に、今回のプログラムで必要となる using 宣言を記述します。
Visual Studio を使っていれば、自動補完で追加されることも多いですが、ここでは明示的に書いておきましょう。

using Manpuku.Edinet.Xbrl; // 今回の主役です
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System.Data;

🧩 DIでパーサを取り出す
.NETでは、DI(Dependency Injection:依存性注入) という仕組みを使って、
必要なサービス(ここでは IXbrlParser)を柔軟に取得できます。
今回は、Microsoft.Extensions.Hosting を使ってホストを構築し、サービスを登録→取得する流れで進めていきます。

// ホストを構築し、XBRLパーサをサービスとして登録
using IHost host = Host.CreateDefaultBuilder(args)
    .ConfigureServices((_, services) =>
    {
        services.AddTransient<IXbrlParser, XbrlParser>(); // XBRLパーサを登録
    })
    .Build();

// 登録したパーサを取得
var parser = host.Services.GetRequiredService<IXbrlParser>();

このようにしておくと、テストや拡張がしやすく、保守性の高い構造になります。
構文の流れを整えることで、読みやすく、詩的な設計にもつながりますね。

📂 データの読み込みとパース 〜デフォルトの loader を添えて
次に、実際のXBRLファイルを読み込んで、DTS(Discoverable Taxonomy Set)をパースしてみましょう。
今回は、金融庁が公開しているサンプルデータを使います。
以下のページからダウンロードできます:
👉 金融庁HP
その中の「(h) サンプルインスタンス(ZIP:3,920KB)」を取得してください。
ZIPを展開すると、次のようなパスにXBRLファイルが格納されています:

サンプルインスタンス\ダウンロードデータ\02_開示府令-有価証券報告書\S002XXXX\XBRL\PublicDoc

この PublicDoc フォルダ内のファイルを、プロジェクトからアクセスできる場所に置いておきましょう。
では、コードを見ていきます:

// サンプルXBRLファイルのパスを指定
var xbrl = "Path\\To\\PublicDoc\\jpcrp030000-asr-001_X99001-000_2025-03-31_01_2025-06-28.xbrl";

// ローカルパスから URI を生成
var xbrlUri = new Uri(Path.GetFullPath(xbrl));

// XBRLドキュメントをパースして DTS を取得
// デフォルトのローダ関数を使用します
var dts = await parser.ParseAsync(xbrlUri, XbrlParser.DefaultLoaderFunc);

DefaultLoaderFunc は、UriからXMLファイルをロードする基本的なローダです。
必要に応じて、独自のローダ関数を渡すことで、キャッシュや認証付きアクセスにも対応できます。

📊 値(ファクト)とコンテキストの表
パースして得られた dts(Discoverable Taxonomy Set)から、
ファクト(値)を要素(コンセプト)ごとにまとめて、値とそのコンテキスト情報を出力してみましょう。
この処理では、XBRLの構文を読み解きながら、「どの項目に、どんな値が、どんな期間で記載されているか」 を確認できます。

// コンセプト(要素)ごとにファクトをグループ化
var factsGroubedByConcept = dts.Facts
    .GroupBy(fact => fact.Concept)
    .ToDictionary(group => group.Key, group => group.ToArray());

// 各コンセプトごとにファクトとそのコンテキストを出力
foreach (var facts in factsGroubedByConcept)
{
    var concept = facts.Key;
    Console.WriteLine($"{concept.Name.LocalName}:");

    foreach (var fact in facts.Value)
    {
        if (fact.Context == null) throw new DataException("Fact context is null.");

        if (fact.Nil)
            Console.WriteLine($"  - Value: null");
        else
            Console.WriteLine($"  - Value: {ShortenValue(fact.Value)}");

        var decimals = fact.Decimals?.ToString() ?? "null";
        Console.WriteLine($"    Decimals: {decimals}");

        var unit = fact.Unit?.Id ?? "null";
        Console.WriteLine($"    Unit: {unit}");

        Console.WriteLine($"    Context:");
        Console.WriteLine($"      ID: {fact.Context.Id}");
        Console.WriteLine($"      StartDate: {EscapeNull(fact.Context.StartDate)}");
        Console.WriteLine($"      EndDate: {EscapeNull(fact.Context.EndDate)}");
        Console.WriteLine($"      Instant: {EscapeNull(fact.Context.Instant)}");

        if (fact.Context.Scenario.Length != 0)
        {
            Console.WriteLine($"      Scenario:");
            foreach (var dim in fact.Context.Scenario)
            {
                Console.WriteLine($"        - {dim.Dimension.Name.LocalName} : {dim.Member.Name.LocalName}");
            }
        }
    }

    Console.WriteLine();
}

このようにして、XBRLに記載された数値や期間、単位、シナリオ情報などを丁寧に読み解くことができます。
まるで、構文の森を歩きながら、意味のかけらを拾い集めていくような感覚です。

🛠 補助関数:構文の余白を整える
出力を見やすくするために、値の整形や null の扱いを補助する関数を用意しておきましょう。
これは、構文の乱れを整えて、読者が意味を読み取りやすくするための小さな工夫です。

// 値を短く整形する(改行除去+空白の圧縮)
static string ShortenValue(string? value, int maxLength = 50)
{
    if (string.IsNullOrWhiteSpace(value))
        return string.Empty;

    // 改行をスペースに置換し、空白を圧縮
    var normalized = System.Text.RegularExpressions.Regex.Replace(value, @"[\r\n]+", " ");
    normalized = System.Text.RegularExpressions.Regex.Replace(normalized, @"\s+", " ").Trim();

    return normalized.Length <= maxLength
        ? normalized
        : normalized[..maxLength] + "...";
}

// nullや空文字を "null" として表示する
static string EscapeNull(string value)
{
    if (string.IsNullOrWhiteSpace(value))
        return "null";
    return value;
}

このような補助関数を添えることで、構文の読みやすさと詩的な整え方の両方を実現できます。

このプログラムを実行すると次のように表示されます。

実行結果
NumberOfSubmissionDEI:
  - Value: 1
    Decimals: 0
    Unit: pure
    Context:
      ID: FilingDateInstant
      StartDate: null
      EndDate: null
      Instant: 2025-06-28

NetSalesSummaryOfBusinessResults:
  - Value: 231282000000
    Decimals: -6
    Unit: JPY
    Context:
      ID: Prior4YearDuration
      StartDate: 2020-04-01
      EndDate: 2021-03-31
      Instant: null
  - Value: 273802000000
    Decimals: -6
    Unit: JPY
    Context:
      ID: Prior3YearDuration
      StartDate: 2021-04-01
      EndDate: 2022-03-31
      Instant: null
  - Value: 303080000000
    Decimals: -6
    Unit: JPY
    Context:
      ID: Prior2YearDuration
      StartDate: 2022-04-01
      EndDate: 2023-03-31
      Instant: null
  - Value: 316934000000
    Decimals: -6
    Unit: JPY
    Context:
      ID: Prior1YearDuration
      StartDate: 2023-04-01
      EndDate: 2024-03-31
      Instant: null
  - Value: 323609000000
    Decimals: -6
    Unit: JPY
    Context:
      ID: CurrentYearDuration
      StartDate: 2024-04-01
      EndDate: 2025-03-31
      Instant: null
  - Value: 130747000000
    Decimals: -6
    Unit: JPY
    Context:
      ID: Prior4YearDuration_NonConsolidatedMember
      StartDate: 2020-04-01
      EndDate: 2021-03-31
      Instant: null
      Scenario:
        - ConsolidatedOrNonConsolidatedAxis : NonConsolidatedMember
  - Value: 154886000000
    Decimals: -6
    Unit: JPY
    Context:
      ID: Prior3YearDuration_NonConsolidatedMember
      StartDate: 2021-04-01
      EndDate: 2022-03-31
      Instant: null
      Scenario:
        - ConsolidatedOrNonConsolidatedAxis : NonConsolidatedMember
  - Value: 183448000000
    Decimals: -6
    Unit: JPY
    Context:
      ID: Prior2YearDuration_NonConsolidatedMember
      StartDate: 2022-04-01
      EndDate: 2023-03-31
      Instant: null
      Scenario:
        - ConsolidatedOrNonConsolidatedAxis : NonConsolidatedMember
  - Value: 196499000000
    Decimals: -6
    Unit: JPY
    Context:
      ID: Prior1YearDuration_NonConsolidatedMember
      StartDate: 2023-04-01
      EndDate: 2024-03-31
      Instant: null
      Scenario:
        - ConsolidatedOrNonConsolidatedAxis : NonConsolidatedMember
  - Value: 210346000000
    Decimals: -6
    Unit: JPY
    Context:
      ID: CurrentYearDuration_NonConsolidatedMember
      StartDate: 2024-04-01
      EndDate: 2025-03-31
      Instant: null
      Scenario:
        - ConsolidatedOrNonConsolidatedAxis : NonConsolidatedMember
(以下略)

XBRLの世界は複雑ですが、こうして一つひとつ丁寧に読み解いていくことで、
金融開示の構文も、詩のように味わえるものになるかもしれません。

💡 このコード全体は GitHub にも掲載しています:
https://github.com/manpukupanda/edinet-xbrl-parser/blob/main/examples/facts/Program.cs


📝 おわりに

このライブラリは、金融開示データという硬質な構文に、詩的な読みやすさと拡張性を与える試みです。
もしあなたがEDINETのXBRLに触れる機会があるなら、ぜひ一度このライブラリを試してみてください。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?