通过 Nancy 将双精度数组从 C# 传递到 JavaScript

Pass array of doubles from C# to JavaScript via Nancy

首先我会说我非常精通 C#,但在 JS 和 Nancy 方面几乎是个新手。我正在尝试将二维双精度数组从 C#.Net Framework 控制台应用程序传递到 http://visjs.org/docs/graph3d/ 处的 Graph3d 包。 Graph3d 示例代码工作正常。我可以修改他们的 JS 来做一个简单的 5x5 数组,所有数组值都设置为 2,除了数组 (2,2) 的中间值为 22。它按预期绘制图形:

绘制上图的JS代码如下:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
  <title>Graph 3D demo</title>

  <style>
    html, body {
      font: 10pt arial;
      padding: 0;
      margin: 0;
      width: 100%;
      height: 100%;
    }

    #mygraph {
      position: absolute;
      width: 100%;
      height: 100%;
    }
  </style>

  <!-- for mobile devices like android and iphone -->
  <meta name="viewport" content="target-densitydpi=device-dpi, width=device-width" />

  <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/vis/4.21.0/vis.min.js"></script>

  <script type="text/javascript">
    var data = null;
    var graph = null;

    var request = new XMLHttpRequest();

    request.open('GET', 'http://192.168.1.153:1234/', true);
    request.onload = function () {
        // Begin accessing JSON data here
        //var data = JSON.parse(this.response);
        console.log(this.response);
    }

    request.send();

    // Called when the Visualization API is loaded.
    function drawVisualization() {
      // Create and populate a data table.
      data = new vis.DataSet();
      data.add([{x:0,y:0,z:2}]);
      data.add([{x:0,y:1,z:2}]);
      data.add([{x:0,y:2,z:2}]);
      data.add([{x:0,y:3,z:2}]);
      data.add([{x:0,y:4,z:2}]);
      data.add([{x:1,y:0,z:2}]);
      data.add([{x:1,y:1,z:2}]);
      data.add([{x:1,y:2,z:2}]);
      data.add([{x:1,y:3,z:2}]);
      data.add([{x:1,y:4,z:2}]);
      data.add([{x:2,y:0,z:2}]);
      data.add([{x:2,y:1,z:2}]);
      data.add([{x:2,y:2,z:22}]);
      data.add([{x:2,y:3,z:2}]);
      data.add([{x:2,y:4,z:2}]);
      data.add([{x:3,y:0,z:2}]);
      data.add([{x:3,y:1,z:2}]);
      data.add([{x:3,y:2,z:2}]);
      data.add([{x:3,y:3,z:2}]);
      data.add([{x:3,y:4,z:2}]);
      data.add([{x:4,y:0,z:2}]);
      data.add([{x:4,y:1,z:2}]);
      data.add([{x:4,y:2,z:2}]);
      data.add([{x:4,y:3,z:2}]);
      data.add([{x:4,y:4,z:2}]);

      // specify options
      var options = {
        width:  '100%',
        height: '100%',
        style: 'surface',
        showPerspective: true,
        showGrid: true,
        showShadow: false,
        keepAspectRatio: true,
        verticalRatio: 0.5,
        backgroundColor: {
          strokeWidth: 0
        }
      };

      // create our graph
      var container = document.getElementById('mygraph');
      graph = new vis.Graph3d(container, data, options);
    }
  </script>

</head>

<body onresize="graph.redraw();" onload="drawVisualization()">
<div id="mygraph"></div>
</body>
</html>

非常简单。

但是,当我尝试将相同的 5x5 数组从 C# 通过 Nancy 发送到 JS 时,图片看起来不一样了。数据数组是正确的,由 Firefox 中的控制台 window 验证。这是错误的图片:

这是使用 Nancy 运行 网络服务器并将数组传递给 JS 的 C# 代码:

using System;
using System.IO;
using System.Reflection;
using System.Text;
using Nancy;
using Nancy.ModelBinding;
using Nancy.Conventions;
using Nancy.Hosting.Self;

namespace test3x3graph
{
    class Program
    {
        static void Main(string[] args)
        {
            //Start Nancy web server
            Nancy.Json.JsonSettings.MaxJsonLength = int.MaxValue;
            string nancyUri = "http://localhost:4143/";
            NancyHost host = new NancyHost(new Uri(nancyUri));
            host.Start();
            Console.WriteLine("NancyFx is running on " + nancyUri);
            Console.WriteLine("\nPress any key to exit");
            Console.ReadKey();
        }
    }

    public class CustomRootPathProvider : IRootPathProvider
    {
        public string GetRootPath()
        {
            return Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
        }
    }

    public class CustomBootstrapper : DefaultNancyBootstrapper
    {
        protected override IRootPathProvider RootPathProvider
        {
            get { return new CustomRootPathProvider(); }
        }

        protected override void ConfigureConventions(NancyConventions conventions)
        {
            base.ConfigureConventions(conventions);

            conventions.StaticContentsConventions.Add(
                StaticContentConventionBuilder.AddDirectory("public", @"public")
            );
        }
    }

    public class SurfaceModel
    {
        public static string SurfaceData = string.Empty;
        //public static double[,] SurfaceData;

        //static constructor to generate sample data
        static SurfaceModel()
        {
            const int size = 5;
            double[,] data = new double[size, size];
            for (int i = 0; i < size; i++)
            {
                for (int j = 0; j < size; j++)
                {
                    data[i, j] = 2;
                }
            }
            data[2, 2] = 22;
            //SurfaceData = data;
            //convert to string representation
            StringBuilder sb = new StringBuilder();
            sb.Append("[");
            for (int i = 0; i < size; i++)
            {
                for (int j = 0; j < size; j++)
                {
                    sb.Append("[");
                    sb.Append(i.ToString());
                    sb.Append(",");
                    sb.Append(j.ToString());
                    sb.Append(",");
                    sb.Append(data[i, j].ToString());
                    sb.Append("]");
                    if (j < size - 1)
                        sb.Append(",");
                }
                if (i < size - 1)
                    sb.Append(",");
            }
            sb.Append("]");
            SurfaceData = sb.ToString();
        }
    }

    public class TestModule : Nancy.NancyModule
    {
        public TestModule()
        {
            Get["/"] = args =>
            {
                SurfaceModel model = this.Bind<SurfaceModel>();
                return View["surface", model];
            };
        }
    }
}

最后,这是从 C# 接收表面模型的 JS 代码:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
  <title>Graph3D test</title>

  <style>
    html, body {
      font: 10pt arial;
      padding: 0;
      margin: 0;
      width: 100%;
      height: 100%;
    }

    #mygraph {
      position: absolute;
      width: 100%;
      height: 100%;
    }
  </style>

  <!-- for mobile devices like android and iphone -->
  <meta name="viewport" content="target-densitydpi=device-dpi, width=device-width" />

  <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/vis/4.21.0/vis.min.js"></script>

  <script type="text/javascript">
    var data = null;
    var graph = null;

    // inject surface model here...
    var surfaceData = @Model.SurfaceData;

    // Called when the Visualization API is loaded.
    function drawVisualization() {
        console.log(surfaceData);
      // Create and populate a data table.
      data = new vis.DataSet();

      for(var x = 0; x < surfaceData.length; x++) {
        for(var y = 0; y < surfaceData[x].length; y++) {
            data.add([
                {
                    x: x,
                    y: y,
                    z: surfaceData[x][y]
                }
            ]);
        }
      }

      // specify options
      var options = {
        width:  '100%',
        height: '100%',
        style: 'surface',
        showPerspective: true,
        showGrid: true,
        showShadow: false,
        keepAspectRatio: true,
        verticalRatio: 0.5,
        backgroundColor: {
          strokeWidth: 0
        }
      };

      // create our graph
      var container = document.getElementById('mygraph');
      graph = new vis.Graph3d(container, data, options);
    }
  </script>

</head>

<body onresize="graph.redraw();" onload="drawVisualization()">
<div id="mygraph"></div>
</body>
</html>

该 JS 与有效的 JS 几乎相同;只有数据部分不同。所有的 Graph3d 选项都是相同的,所以我很困惑,图片渲染不一样。

另请注意,在 C# 中,double 数组在发送到 JS 之前已转换为字符串表示形式。当我尝试将它作为双打数组传递时,生成的图片是空白的。如果可以使用字符串表示找到解决方案,那就太棒了。但是如果能够避免中间步骤并将双精度数组直接传递给 JS,那就更干净了。

感谢任何建议。

我能够使用 Json.NET 序列化程序来实现它。令我非常惊讶的是 JsonConvert.SerializeObject 没有添加允许 JSON.Parse 反序列化字符串而不会出错的单引号。

无论如何,这里是相关的 C# 代码:

    //create a 1-dimensional array to send to Javascript.
    //the first item is the number of rows; the 2nd item is the number of columns;
    //the remaining items are the values from the 2-d array in row-major order
    double[] data1d = new double[size*size + 2];
    data1d[0] = data1d[1] = size;
    int k = 2;
    for (int i = 0; i < size; i++)
    {
        for (int j = 0; j < size; j++)
        {
            data1d[k++] = data[i, j];
        }
    }

    SurfaceData = "'" + JsonConvert.SerializeObject(data1d) + "'";

这里是相关的 Javascript 代码:

// inject surface model here.
// Model.Surface data is a 2-dimensional array stored in a 1-dimensional array as follows:
// Array item [0] is the number of rows.  Array item [1] is the number of columns.
// The remaining array items are the values from the 2-d array in row-major order
var surfaceData = JSON.parse(@Model.SurfaceData);

// Called when the Visualization API is loaded.
function drawVisualization() {
    console.log(surfaceData);
  // Create and populate a data table.
  data = new vis.DataSet();
  var k = 2;
  for(var x = 0; x < surfaceData[0]; x++) {
    for(var y = 0; y < surfaceData[1]; y++) {
        data.add([{x: x, y: y, z: surfaceData[k++]}]);
    }
  }

您可以提供脚本路径或css路径

Get("/api/scripts/scripts.js", _ => return (Response)File.ReadAllText(scriptPath);

您基本上可以在这里做任何事情来制作内容。