[Node.js] Edge.js を触ってみた

  • 7
    いいね
  • 0
    コメント

[C#] Edge.js を触ってみた で C# 側から Node.js を使うのをメモしましたので、今回は javascript 側から C# を使うところを少しメモしておこうと思います。

Edge.js についての詳細は以下を参照してください。

開発環境

  • Windows 10 Pro
  • Visual Studio Code
  • Node.js v0.12.2

準備

npm を使って Edge.js をインストールします。

npm install edge

Hello World

Hello World してみます。

sample1.js
var edge = require('edge');

var sample1 = edge.func(function () {/*
    async (input) =>
        "Hello " + input.ToString() + "!";
*/});

sample1('World', function (error, result) {
    if (error) throw error;
    console.log(result);
});

edge.func() を使って C# のスクリプトを文字列として引数に渡してあげると、 javascript で使えるようになります。また、引数で与える C# のラムダには async をつける必要があります。

匿名型を受け取る

匿名型を受け取ってみます。

sample2.js
var edge = require('edge');

var sample2 = edge.func(function () {/*
    using System.Collections.Generic;
    using System.Linq;

    async (input) => { 
        var items = ((object[])input).Cast<int>();
        var filter = items.Where(x => x % 2 == 0);
        var map = items.Select(x => x * 2);
        var sum = items.Sum();

        Console.WriteLine("-- csharp ---------------------------------------------------");
        Console.WriteLine(string.Join(",", filter));
        Console.WriteLine(string.Join(",", map));
        Console.WriteLine(sum);

        return new
        {
            filter = filter,
            map = map,
            sum = sum
        };
    };
*/});

sample2([1, 2, 3, 4, 5], function (error, result) {
    if (error) throw error;
    console.log('-- javascript -----------------------------------------------')
    console.log(result);
});

javascript 側から 配列を引数で渡して、C# 側でごにょごにょしてから匿名型にして返してます。
上の例では System.Collections.Generic 名前空間と System.Linq 名前区間を使っているので先頭で using しています。

クラスを定義して使う場合

クラスを定義する必要がある場合は以下のように記述します。

using System.Threading.Tasks;

public class Startup
{
    public async Task<object> Invoke(object input)
    {
        ...
    }
}

class Sample
{
    ...
}

Startup クラスの Invoke() メソッドが javascript で edge.func() したときに呼ばれる部分となります。

また、ラムダで記述する場合は SystemSystem.Threading.Tasks はデフォルトで using されているようですが、クラスを定義して使用する場合は上の例のように自分で using する必要があります。

クラスを受取る

クラスを定義して受取ってみます。

sample3
var edge = require('edge');

var sample3 = edge.func(function () {/*
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;

    public class Startup
    {
        public async Task<object> Invoke(object input)
        {
            var items = ((object[])input).OfType<int>();
            var filter = items.Where(x => x % 2 == 0);
            var map = items.Select(x => x * 2);
            var sum = items.Sum();

            Console.WriteLine("-- csharp ---------------------------------------------------");
            Console.WriteLine(string.Join(",", filter));
            Console.WriteLine(string.Join(",", map));
            Console.WriteLine(sum);

            return new Sample
            {
                Filter = filter,
                Map = map,
                Sum = sum
            };
        }
    }

    public class Sample
    {
        public IEnumerable<int> Filter { get; set; }
        public IEnumerable<int> Map { get; set; }
        public int Sum { get; set; }
    }
*/});

sample3([1, 2, 3, 4, 5], function (error, result) {
    if (error) throw error;
    console.log('-- javascript -----------------------------------------------')
    console.log(result);
});

Sample クラスを作成して返してます。それ以外は匿名型の時と同じです。

cs/csx ファイルの読込

C# のコードは別ファイルにすることもできます。javascript 側で呼び出すときは以下のようにします。

var sample4 = edge.func(require('path').join(__dirname, 'sample4.cs'));

Win32 API

呼べるらしいので試します。

C#

beep.cs
using System.Runtime.InteropServices;
using System.Threading.Tasks;

public class Startup
{
    [DllImport("kernel32.dll")]
    private extern static bool Beep(uint dwFreq, uint dwDuration);

    public async Task<object> Invoke(dynamic input)
    {
        Beep((uint)input.freq, (uint)input.duration);
        return null;
    }
}

javascript

sample5.js
var edge = require('edge');

var beep = edge.func(require('path').join(__dirname, 'beep.cs'));

beep({ freq : 262, duration : 500}, function (error, result) {
    if (error) throw error;
});

kernel32.dll にあるBeep関数を呼び出して音を鳴らしています。Beep関数は音の高さと長さを指定することができるので javascript 側で高さと長さを指定してあげるために連想配列にして渡しています。

javascript 側から引数で連想配列を渡すときなど、 Invoke() メソッドの引数の型を object 型 の代わりに dynamic 型にすることも可能です。

XML

XMLファイルの内容をC# 側でオブジェクトにデシリアライズしてみます。

XMLファイルの内容

sample.xml
<?xml version="1.0" encoding="utf-8"?>
<members>
  <person id="1">
    <name>太郎</name>
    <age>16</age>
  </person>
  <person id="2">
    <name>次郎</name>
    <age>35</age>
  </person>
  <person id="3">
    <name>三郎</name>
    <age>21</age>
  </person>
  <person id="4">
    <name>ケビン</name>
    <age>42</age>
  </person>
</members>

members 要素をルートとして person 要素がずらずらと並ぶ感じです。

C#

sample.cs
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using System.Xml.Serialization;

public class Startup
{
    public async Task<object> Invoke(string path)
    {
        // ファイルを開く
        using (var stream = new FileStream(path, FileMode.Open))
        {
            // デシリアライズする
            var serializer = new XmlSerializer(typeof(Members));
            var model = (Members)serializer.Deserialize(stream);

            // person 要素を返す
            return model.Person;
        }
    }

    [XmlRoot("members")]
    public class Members
    {
        [XmlElement("person")]
        public List<Person> Person { get; set; }
    }

    public class Person
    {
        [XmlAttribute("id")]
        public int Id { get; set; }

        [XmlElement("name")]
        public string Name { get; set; }

        [XmlElement("age")]
        public int Age { get; set; }
    }
}

XMLドキュメントの内容に合うようにモデルを用意してデシリアライズしています。
デシリアライズした結果から person 要素にあたる部分のコレクションを返しています。

javascript

sample6.js
var edge = require('edge');

var xml = edge.func({
    source : require('path').join(__dirname, 'sample.cs'),
    references : ["System.Xml.dll"]
});

xml(require('path').join(__dirname, 'sample.xml'), function (error, result) {
    if (error) throw error;
    result.forEach(function(element) {
        console.log("id:" + element.Id + ", name: " + element.Name + ", age: " + element.Age);
    }, this);
});

C# 側で System.Xml.Serialization 名前空間を使っているので System.Xml.dll への参照を references に追加しています。

.NET を使うと jQuery とは一味違った感じでXML操作ができそうです。

他にも

公式のサイトでは System.Drawing を使って画像ファイルの形式を変えて保存したり、Zipファイルを生成したり、 ADO.NET を使ってデータベースに接続したりする方法などいろいろ紹介されています。

まとめ

Edge.js を利用すれば Node.js で .NET のライブラリや Windows であれば Win32 API が使えます。また、今回は C# でやりましたが、 F# や Python (IronPython) など .NET 上で動く言語であれば利用できます。

簡単なメモ書きですが Edge.js に興味を持っていただけたら幸いです。

参考

Edge.js
http://tjanczuk.github.io/edge/#/

GitHub - tjanczuk/edge: Run .NET and Node.js code in-process on Windows, MacOS, and Linux
https://github.com/tjanczuk/edge/tree/master