InteropTests.Json.cs 6.3 KB

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