123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351 |
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using Jint.Native.Array;
- using Jint.Native.Global;
- using Jint.Native.Object;
- using Jint.Runtime;
- using Jint.Runtime.Descriptors;
- namespace Jint.Native.Json
- {
- public class JsonSerializer
- {
- private readonly Engine _engine;
- public JsonSerializer(Engine engine)
- {
- _engine = engine;
- }
- Stack<object> _stack;
- string _indent, _gap;
- List<string> _propertyList;
- JsValue _replacerFunction = Undefined.Instance;
- public JsValue Serialize(JsValue value, JsValue replacer, JsValue space)
- {
- _stack = new Stack<object>();
- if (replacer.IsObject())
- {
- if (replacer.Is<ICallable>())
- {
- _replacerFunction = replacer;
- }
- else
- {
- var replacerObj = replacer.AsObject();
- if (replacerObj.Class == "Array")
- {
- _propertyList = new List<string>();
- }
- foreach (var property in replacerObj.Properties.Values)
- {
- JsValue v = _engine.GetValue(property);
- string item = null;
- if (v.IsString())
- {
- item = v.AsString();
- }
- else if (v.IsNumber())
- {
- item = TypeConverter.ToString(v);
- }
- else if (v.IsObject())
- {
- var propertyObj = v.AsObject();
- if (propertyObj.Class == "String" || propertyObj.Class == "Number")
- {
- item = TypeConverter.ToString(v);
- }
- }
- if (item != null && !_propertyList.Contains(item))
- {
- _propertyList.Add(item);
- }
- }
- }
- }
- if (space.IsObject())
- {
- var spaceObj = space.AsObject();
- if (spaceObj.Class == "Number")
- {
- space = TypeConverter.ToNumber(spaceObj);
- }
- else if (spaceObj.Class == "String")
- {
- space = TypeConverter.ToString(spaceObj);
- }
- }
- // defining the gap
- if (space.IsNumber())
- {
- _gap = new System.String(' ', (int) System.Math.Min(10, space.AsNumber()));
- }
- else if (space.IsString())
- {
- var stringSpace = space.AsString();
- _gap = stringSpace.Length <= 10 ? stringSpace : stringSpace.Substring(0, 10);
- }
- else
- {
- _gap = "";
- }
- var wrapper = _engine.Object.Construct(Arguments.Empty);
- wrapper.DefineOwnProperty("", new PropertyDescriptor(value, true, true, true), false);
- return Str("", wrapper);
- }
- private JsValue Str(string key, ObjectInstance holder)
- {
-
- var value = holder.Get(key);
- if (value.IsObject())
- {
- var toJson = value.AsObject().Get("toJSON");
- if (toJson.IsObject())
- {
- var callableToJson = toJson.AsObject() as ICallable;
- if (callableToJson != null)
- {
- value = callableToJson.Call(value, Arguments.From(key));
- }
- }
- }
-
- if (_replacerFunction != Undefined.Instance)
- {
- var replacerFunctionCallable = (ICallable)_replacerFunction.AsObject();
- value = replacerFunctionCallable.Call(holder, Arguments.From(key, value)); // 15.12.3-11-12 added missing value parameter
- }
-
- if (value.IsObject())
- {
- var valueObj = value.AsObject();
- switch (valueObj.Class)
- {
- case "Number":
- value = TypeConverter.ToNumber(value);
- break;
- case "String":
- value = TypeConverter.ToString(value);
- break;
- case "Boolean":
- value = TypeConverter.ToPrimitive(value);
- break;
- case "Array": // 15.12.3-11-12 added missing serialization of array
- value = SerializeArray(value.As<ArrayInstance>());
- return value;
- break;
- }
- }
-
- if (value == Undefined.Instance) {
- return Undefined.Instance; // 15.12.3-11-10
- }
- if (value == Null.Instance)
- {
- return "null";
- }
- if (value.IsBoolean() && value.AsBoolean())
- {
- return "true";
- }
- if (value.IsBoolean() && !value.AsBoolean())
- {
- return "false";
- }
- if (value.IsString())
- {
- return Quote(value.AsString());
- }
- if (value.IsNumber())
- {
- if (GlobalObject.IsFinite(Undefined.Instance, Arguments.From(value)).AsBoolean())
- {
- return TypeConverter.ToString(value);
- }
-
- return "null";
- }
- if (value.IsObject())
- {
- var valueCallable = value.AsObject() as ICallable;
- if (valueCallable != null)
- {
- if (value.AsObject().Class == "Array")
- {
- return SerializeArray(value.As<ArrayInstance>());
- }
- return SerializeObject(value.AsObject());
- }
- }
- return "null";
- }
- private string Quote(string value)
- {
- var product = "\"";
- foreach (char c in value)
- {
- switch (c)
- {
- case '\"':
- product += "\\\"";
- break;
- case '\\':
- product += "\\\\";
- break;
- case '\b':
- product += "\\b";
- break;
- case '\f':
- product += "\\f";
- break;
- case '\n':
- product += "\\n";
- break;
- case '\r':
- product += "\\r";
- break;
- case '\t':
- product += "\\t";
- break;
- default:
- if (c < 0x20)
- {
- product += "\\u";
- product += ((int) c).ToString("x4");
- }
- else
- product += c;
- break;
- }
- }
- product += "\"";
- return product;
- }
- private string SerializeArray(ArrayInstance value)
- {
- EnsureNonCyclicity(value);
- _stack.Push(value);
- var stepback = _indent;
- _indent = _indent + _gap;
- var partial = new List<string>();
- var len = TypeConverter.ToUint32(value.Get("length"));
- for (int i = 0; i < len; i++)
- {
- var strP = Str(TypeConverter.ToString(i), value);
- partial.Add(strP.AsString()); // 15.12.3-11-12
- }
- if (partial.Count == 0)
- {
- return "[]";
- }
- string final;
- if (_gap == "")
- {
- var separator = ",";
- var properties = System.String.Join(separator, partial.ToArray());
- final = "[" + properties + "]";
- }
- else
- {
- var separator = ",\n" + _indent;
- var properties = System.String.Join(separator, partial.ToArray());
- final = "[\n" + _indent + properties + "\n" + stepback + "]";
- }
-
- _stack.Pop();
- _indent = stepback;
- return final;
- }
- private void EnsureNonCyclicity(object value)
- {
- if (value == null)
- {
- throw new ArgumentNullException("value");
- }
- if (_stack.Contains(value))
- {
- throw new JavaScriptException(_engine.TypeError, "Cyclic reference detected.");
- }
- }
- private string SerializeObject(ObjectInstance value)
- {
- string final;
- EnsureNonCyclicity(value);
- _stack.Push(value);
- var stepback = _indent;
- _indent += _gap;
- var k = _propertyList;
- if (k == null)
- {
- k = value.Properties.Where(x => x.Value.Enumerable.Value.AsBoolean()).Select(x => x.Key).ToList();
- }
- var partial = new List<string>();
- foreach (var p in k)
- {
- var strP = Str(p, value);
- if (strP != "null")
- {
- var member = Quote(p) + ":";
- if (_gap != "")
- {
- member += " ";
- }
- member += strP;
- partial.Add(member);
- }
- }
- if (partial.Count == 0)
- {
- final = "{}";
- }
- else
- {
- if (_gap == "")
- {
- var separator = ",";
- var properties = System.String.Join(separator, partial.ToArray());
- final = "{" + properties + "}";
- }
- else
- {
- var separator = ",\n" + _indent;
- var properties = System.String.Join(separator, partial.ToArray());
- final = "{\n" + _indent + properties + "\n" + stepback + "}";
- }
- }
- _stack.Pop();
- _indent = stepback;
- return final;
- }
- }
- }
|