InteropTests.SystemTextJson.cs 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. using System.Text.Json.Nodes;
  2. using Jint.Native;
  3. using Jint.Runtime.Interop;
  4. using System.Text.Json;
  5. namespace Jint.Tests.PublicInterface;
  6. public partial class InteropTests
  7. {
  8. [Fact]
  9. public void ArrayPrototypeFindWithInteropJsonArray()
  10. {
  11. var engine = GetEngine();
  12. var array = new JsonArray { "A", "B", "C" };
  13. engine.SetValue("array", array);
  14. Assert.Equal(1, engine.Evaluate("array.findIndex((x) => x === 'B')"));
  15. Assert.Equal('B', engine.Evaluate("array.find((x) => x === 'B')"));
  16. }
  17. [Fact]
  18. public void ArrayPrototypePushWithInteropJsonArray()
  19. {
  20. var engine = GetEngine();
  21. var array = new JsonArray { "A", "B", "C" };
  22. engine.SetValue("array", array);
  23. engine.Evaluate("array.push('D')");
  24. Assert.Equal(4, array.Count);
  25. Assert.Equal("D", array[3]?.ToString());
  26. Assert.Equal(3, engine.Evaluate("array.lastIndexOf('D')"));
  27. }
  28. [Fact]
  29. public void ArrayPrototypePopWithInteropJsonArray()
  30. {
  31. var engine = GetEngine();
  32. var array = new JsonArray { "A", "B", "C" };
  33. engine.SetValue("array", array);
  34. Assert.Equal(2, engine.Evaluate("array.lastIndexOf('C')"));
  35. Assert.Equal(3, array.Count);
  36. Assert.Equal("C", engine.Evaluate("array.pop()"));
  37. Assert.Equal(2, array.Count);
  38. Assert.Equal(-1, engine.Evaluate("array.lastIndexOf('C')"));
  39. }
  40. [Fact]
  41. public void AccessingJsonNodeShouldWork()
  42. {
  43. const string Json = """
  44. {
  45. "falseValue": false,
  46. "employees": {
  47. "trueValue": true,
  48. "falseValue": false,
  49. "number": 123.456,
  50. "zeroNumber": 0,
  51. "emptyString":"",
  52. "nullValue":null,
  53. "other": "abc",
  54. "type": "array",
  55. "value": [
  56. {
  57. "firstName": "John",
  58. "lastName": "Doe"
  59. },
  60. {
  61. "firstName": "Jane",
  62. "lastName": "Doe"
  63. }
  64. ]
  65. }
  66. }
  67. """;
  68. var variables = JsonNode.Parse(Json);
  69. var engine = GetEngine();
  70. engine
  71. .SetValue("falseValue", false)
  72. .SetValue("variables", variables)
  73. .Execute("""
  74. function populateFullName() {
  75. return variables['employees'].value.map(item => {
  76. var newItem =
  77. {
  78. "firstName": item.firstName,
  79. "lastName": item.lastName,
  80. "fullName": item.firstName + ' ' + item.lastName
  81. };
  82. return newItem;
  83. });
  84. }
  85. """);
  86. // reading data
  87. var result = engine.Evaluate("populateFullName()").AsArray();
  88. Assert.Equal((uint) 2, result.Length);
  89. Assert.Equal("John Doe", result[0].AsObject()["fullName"]);
  90. Assert.Equal("Jane Doe", result[1].AsObject()["fullName"]);
  91. Assert.True(engine.Evaluate("variables.employees.trueValue == true").AsBoolean());
  92. Assert.True(engine.Evaluate("variables.employees.number == 123.456").AsBoolean());
  93. Assert.True(engine.Evaluate("variables.employees.other == 'abc'").AsBoolean());
  94. // mutating data via JS
  95. engine.Evaluate("variables.employees.type = 'array2'");
  96. engine.Evaluate("variables.employees.value[0].firstName = 'Jake'");
  97. //Assert.Equal("array2", engine.Evaluate("variables['employees']['type']").ToString());
  98. result = engine.Evaluate("populateFullName()").AsArray();
  99. Assert.Equal((uint) 2, result.Length);
  100. Assert.Equal("Jake Doe", result[0].AsObject()["fullName"]);
  101. // Validate boolean value in the if condition.
  102. Assert.Equal(1, engine.Evaluate("if(!falseValue){ return 1 ;} else {return 0;}").AsNumber());
  103. Assert.Equal(1, engine.Evaluate("if(falseValue===false){ return 1 ;} else {return 0;}").AsNumber());
  104. Assert.True(engine.Evaluate("!variables.zeroNumber").AsBoolean());
  105. Assert.True(engine.Evaluate("!variables.emptyString").AsBoolean());
  106. Assert.True(engine.Evaluate("!variables.nullValue").AsBoolean());
  107. var result2 = engine.Evaluate("!variables.falseValue");
  108. var result3 = engine.Evaluate("!falseValue");
  109. var result4 = engine.Evaluate("variables.falseValue");
  110. var result5 = engine.Evaluate("falseValue");
  111. Assert.NotNull(result2);
  112. Assert.Equal(1, engine.Evaluate("if(variables.falseValue===false){ return 1 ;} else {return 0;}").AsNumber());
  113. Assert.Equal(1, engine.Evaluate("if(falseValue===variables.falseValue){ return 1 ;} else {return 0;}").AsNumber());
  114. Assert.Equal(1, engine.Evaluate("if(!variables.falseValue){ return 1 ;} else {return 0;}").AsNumber());
  115. Assert.Equal(1, engine.Evaluate("if(!variables.employees.falseValue){ return 1 ;} else {return 0;}").AsNumber());
  116. Assert.Equal(0, engine.Evaluate("if(!variables.employees.trueValue) return 1 ; else return 0;").AsNumber());
  117. // mutating original object that is wrapped inside the engine
  118. variables["employees"]["trueValue"] = false;
  119. variables["employees"]["number"] = 456.789;
  120. variables["employees"]["other"] = "def";
  121. variables["employees"]["type"] = "array";
  122. variables["employees"]["value"][0]["firstName"] = "John";
  123. Assert.Equal("array", engine.Evaluate("variables['employees']['type']").ToString());
  124. result = engine.Evaluate("populateFullName()").AsArray();
  125. Assert.Equal((uint) 2, result.Length);
  126. Assert.Equal("John Doe", result[0].AsObject()["fullName"]);
  127. Assert.True(engine.Evaluate("variables.employees.trueValue == false").AsBoolean());
  128. Assert.True(engine.Evaluate("variables.employees.number == 456.789").AsBoolean());
  129. Assert.True(engine.Evaluate("variables.employees.other == 'def'").AsBoolean());
  130. }
  131. private static Engine GetEngine()
  132. {
  133. var engine = new Engine(options =>
  134. {
  135. #if !NET8_0_OR_GREATER
  136. // Jint doesn't know about the types statically as they are not part of the out-of-the-box experience
  137. options.AddObjectConverter(SystemTextJsonValueConverter.Instance);
  138. #endif
  139. });
  140. return engine;
  141. }
  142. }
  143. file sealed class SystemTextJsonValueConverter : IObjectConverter
  144. {
  145. public static readonly SystemTextJsonValueConverter Instance = new();
  146. private SystemTextJsonValueConverter()
  147. {
  148. }
  149. public bool TryConvert(Engine engine, object value, out JsValue result)
  150. {
  151. if (value is JsonValue jsonValue)
  152. {
  153. var valueKind = jsonValue.GetValueKind();
  154. switch (valueKind)
  155. {
  156. case JsonValueKind.Object:
  157. case JsonValueKind.Array:
  158. result = JsValue.FromObject(engine, jsonValue);
  159. break;
  160. case JsonValueKind.String:
  161. result = jsonValue.ToString();
  162. break;
  163. case JsonValueKind.Number:
  164. result = jsonValue.TryGetValue<double>(out var doubleValue) ? JsNumber.Create(doubleValue) : JsValue.Undefined;
  165. break;
  166. case JsonValueKind.True:
  167. result = JsBoolean.True;
  168. break;
  169. case JsonValueKind.False:
  170. result = JsBoolean.False;
  171. break;
  172. case JsonValueKind.Undefined:
  173. result = JsValue.Undefined;
  174. break;
  175. case JsonValueKind.Null:
  176. result = JsValue.Null;
  177. break;
  178. default:
  179. result = JsValue.Undefined;
  180. break;
  181. }
  182. return true;
  183. }
  184. result = JsValue.Undefined;
  185. return false;
  186. }
  187. }