InteropTests.Json.cs 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. using System.Dynamic;
  2. using Jint.Runtime.Interop;
  3. namespace Jint.Tests.PublicInterface;
  4. public partial class InteropTests
  5. {
  6. [Fact]
  7. public void EngineShouldStringifyAnExpandoObjectCorrectly()
  8. {
  9. var engine = new Engine();
  10. dynamic expando = new ExpandoObject();
  11. expando.foo = 5;
  12. expando.bar = "A string";
  13. engine.SetValue(nameof(expando), expando);
  14. var result = engine.Evaluate($"JSON.stringify({nameof(expando)})").AsString();
  15. Assert.Equal("{\"foo\":5,\"bar\":\"A string\"}", result);
  16. }
  17. [Fact]
  18. public void EngineShouldStringifyAnExpandoObjectWithValuesCorrectly()
  19. {
  20. // https://github.com/sebastienros/jint/issues/995
  21. var engine = new Engine();
  22. dynamic expando = new ExpandoObject();
  23. expando.Values = 1;
  24. engine.SetValue("expando", expando);
  25. Assert.Equal("{\"Values\":1}", engine.Evaluate($"JSON.stringify(expando)").AsString());
  26. }
  27. [Fact]
  28. public void EngineShouldStringifyAnJObjectArrayWithValuesCorrectly()
  29. {
  30. //https://github.com/OrchardCMS/OrchardCore/issues/10648
  31. var engine = new Engine();
  32. var queryResults = new List<dynamic>();
  33. queryResults.Add(new { Text = "Text1", Value = 1 });
  34. queryResults.Add(new { Text = "Text2", Value = 2 });
  35. engine.SetValue("testSubject", queryResults.ToArray());
  36. var fromEngine2 = engine.Evaluate("return JSON.stringify(testSubject);");
  37. var result2 = fromEngine2.ToString();
  38. Assert.Equal("[{\"Text\":\"Text1\",\"Value\":1},{\"Text\":\"Text2\",\"Value\":2}]", result2);
  39. }
  40. [Fact]
  41. public void EngineShouldStringifyDynamicObjectListWithValuesCorrectly()
  42. {
  43. var engine = new Engine();
  44. var source = new dynamic[] { new { Text = "Text1", Value = 1 }, new { Text = "Text2", Value = 2 } };
  45. var objects = source.ToList();
  46. engine.SetValue("testSubject", objects);
  47. var fromEngine = engine.Evaluate("return JSON.stringify(testSubject);");
  48. var result = fromEngine.ToString();
  49. Assert.Equal("[{\"Text\":\"Text1\",\"Value\":1},{\"Text\":\"Text2\",\"Value\":2}]", result);
  50. }
  51. [Fact]
  52. public void EngineShouldStringifyDynamicObjectArrayWithValuesCorrectly()
  53. {
  54. var engine = new Engine();
  55. var source = new dynamic[] { new { Text = "Text1", Value = 1 }, new { Text = "Text2", Value = 2 } };
  56. engine.SetValue("testSubject", source.AsEnumerable());
  57. var fromEngine = engine.Evaluate("return JSON.stringify(testSubject);");
  58. var result = fromEngine.ToString();
  59. Assert.Equal("[{\"Text\":\"Text1\",\"Value\":1},{\"Text\":\"Text2\",\"Value\":2}]", result);
  60. }
  61. [Fact]
  62. public void CanStringifyTimeSpanUsingCustomToJsonHook()
  63. {
  64. var engine = new Engine(options =>
  65. {
  66. options.Interop.MemberAccessor = (e, target, member) =>
  67. {
  68. // custom serializer hook for known object types
  69. if (member == "toJSON" && target is TimeSpan)
  70. {
  71. return new ClrFunction(e, "toJSON", (thisObject, _) =>
  72. {
  73. // here could check other types too
  74. var wrappedInstance = ((IObjectWrapper) thisObject).Target;
  75. return wrappedInstance.ToString();
  76. });
  77. }
  78. return null;
  79. };
  80. });
  81. var expected = Newtonsoft.Json.JsonConvert.SerializeObject(TimeSpan.FromSeconds(3));
  82. engine.SetValue("TimeSpan", TypeReference.CreateTypeReference<TimeSpan>(engine));
  83. var value = engine.Evaluate("JSON.stringify(TimeSpan.FromSeconds(3));");
  84. Assert.Equal(expected, value);
  85. }
  86. [Fact]
  87. public void CanStringifyUsingSerializeToJson()
  88. {
  89. object testObject = new { Foo = "bar", FooBar = new { Foo = 123.45, Foobar = new DateTime(2022, 7, 16, 0, 0, 0, DateTimeKind.Utc) } };
  90. // without interop
  91. var engineNoInterop = new Engine();
  92. engineNoInterop.SetValue("TimeSpan", TypeReference.CreateTypeReference<TimeSpan>(engineNoInterop));
  93. Assert.Throws<Jint.Runtime.JavaScriptException>(
  94. () => engineNoInterop.Evaluate("JSON.stringify(TimeSpan.FromSeconds(3))"));
  95. engineNoInterop.SetValue("TestObject", testObject);
  96. Assert.Equal(
  97. "{\"Foo\":\"bar\",\"FooBar\":{\"Foo\":123.45,\"Foobar\":\"2022-07-16T00:00:00.000Z\"}}",
  98. engineNoInterop.Evaluate("JSON.stringify(TestObject)"));
  99. // interop using Newtonsoft serializer, for example with snake case naming
  100. string Serialize(object o) =>
  101. Newtonsoft.Json.JsonConvert.SerializeObject(o,
  102. new Newtonsoft.Json.JsonSerializerSettings {
  103. ContractResolver = new Newtonsoft.Json.Serialization.DefaultContractResolver {
  104. NamingStrategy = new Newtonsoft.Json.Serialization.SnakeCaseNamingStrategy() } });
  105. var engine = new Engine(options =>
  106. {
  107. options.Interop.SerializeToJson = Serialize;
  108. });
  109. engine.SetValue("TimeSpan", TypeReference.CreateTypeReference<TimeSpan>(engine));
  110. engine.SetValue("TestObject", testObject);
  111. var expected = Serialize(TimeSpan.FromSeconds(3));
  112. var actual = engine.Evaluate("JSON.stringify(TimeSpan.FromSeconds(3));");
  113. Assert.Equal(expected, actual);
  114. expected = Serialize(testObject);
  115. actual = engine.Evaluate("JSON.stringify(TestObject)");
  116. Assert.Equal(expected, actual);
  117. actual = engine.Evaluate("JSON.stringify({ nestedValue: TestObject })");
  118. Assert.Equal($@"{{""nestedValue"":{expected}}}", actual);
  119. }
  120. }