You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

384 lines
12 KiB

  1. #region License
  2. // Copyright (c) 2007 James Newton-King
  3. //
  4. // Permission is hereby granted, free of charge, to any person
  5. // obtaining a copy of this software and associated documentation
  6. // files (the "Software"), to deal in the Software without
  7. // restriction, including without limitation the rights to use,
  8. // copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. // copies of the Software, and to permit persons to whom the
  10. // Software is furnished to do so, subject to the following
  11. // conditions:
  12. //
  13. // The above copyright notice and this permission notice shall be
  14. // included in all copies or substantial portions of the Software.
  15. //
  16. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  17. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  18. // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  19. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  20. // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  21. // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  22. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  23. // OTHER DEALINGS IN THE SOFTWARE.
  24. #endregion
  25. using System;
  26. using System.Collections;
  27. using System.Collections.Generic;
  28. using Newtonsoft.Json.Utilities;
  29. using System.Diagnostics;
  30. using System.Globalization;
  31. namespace Newtonsoft.Json.Linq
  32. {
  33. /// <summary>
  34. /// Represents a JSON property.
  35. /// </summary>
  36. internal partial class JProperty : JContainer
  37. {
  38. #region JPropertyList
  39. private class JPropertyList : IList<JToken>
  40. {
  41. internal JToken _token;
  42. public IEnumerator<JToken> GetEnumerator()
  43. {
  44. if (_token != null)
  45. {
  46. yield return _token;
  47. }
  48. }
  49. IEnumerator IEnumerable.GetEnumerator()
  50. {
  51. return GetEnumerator();
  52. }
  53. public void Add(JToken item)
  54. {
  55. _token = item;
  56. }
  57. public void Clear()
  58. {
  59. _token = null;
  60. }
  61. public bool Contains(JToken item)
  62. {
  63. return (_token == item);
  64. }
  65. public void CopyTo(JToken[] array, int arrayIndex)
  66. {
  67. if (_token != null)
  68. {
  69. array[arrayIndex] = _token;
  70. }
  71. }
  72. public bool Remove(JToken item)
  73. {
  74. if (_token == item)
  75. {
  76. _token = null;
  77. return true;
  78. }
  79. return false;
  80. }
  81. public int Count => (_token != null) ? 1 : 0;
  82. public bool IsReadOnly => false;
  83. public int IndexOf(JToken item)
  84. {
  85. return (_token == item) ? 0 : -1;
  86. }
  87. public void Insert(int index, JToken item)
  88. {
  89. if (index == 0)
  90. {
  91. _token = item;
  92. }
  93. }
  94. public void RemoveAt(int index)
  95. {
  96. if (index == 0)
  97. {
  98. _token = null;
  99. }
  100. }
  101. public JToken this[int index]
  102. {
  103. get => (index == 0) ? _token : null;
  104. set
  105. {
  106. if (index == 0)
  107. {
  108. _token = value;
  109. }
  110. }
  111. }
  112. }
  113. #endregion
  114. private readonly JPropertyList _content = new JPropertyList();
  115. private readonly string _name;
  116. /// <summary>
  117. /// Gets the container's children tokens.
  118. /// </summary>
  119. /// <value>The container's children tokens.</value>
  120. protected override IList<JToken> ChildrenTokens => _content;
  121. /// <summary>
  122. /// Gets the property name.
  123. /// </summary>
  124. /// <value>The property name.</value>
  125. public string Name
  126. {
  127. [DebuggerStepThrough]
  128. get { return _name; }
  129. }
  130. /// <summary>
  131. /// Gets or sets the property value.
  132. /// </summary>
  133. /// <value>The property value.</value>
  134. public JToken Value
  135. {
  136. [DebuggerStepThrough]
  137. get { return _content._token; }
  138. set
  139. {
  140. CheckReentrancy();
  141. JToken newValue = value ?? JValue.CreateNull();
  142. if (_content._token == null)
  143. {
  144. InsertItem(0, newValue, false);
  145. }
  146. else
  147. {
  148. SetItem(0, newValue);
  149. }
  150. }
  151. }
  152. /// <summary>
  153. /// Initializes a new instance of the <see cref="JProperty"/> class from another <see cref="JProperty"/> object.
  154. /// </summary>
  155. /// <param name="other">A <see cref="JProperty"/> object to copy from.</param>
  156. public JProperty(JProperty other)
  157. : base(other)
  158. {
  159. _name = other.Name;
  160. }
  161. internal override JToken GetItem(int index)
  162. {
  163. if (index != 0)
  164. {
  165. throw new ArgumentOutOfRangeException();
  166. }
  167. return Value;
  168. }
  169. internal override void SetItem(int index, JToken item)
  170. {
  171. if (index != 0)
  172. {
  173. throw new ArgumentOutOfRangeException();
  174. }
  175. if (IsTokenUnchanged(Value, item))
  176. {
  177. return;
  178. }
  179. ((JObject)Parent)?.InternalPropertyChanging(this);
  180. base.SetItem(0, item);
  181. ((JObject)Parent)?.InternalPropertyChanged(this);
  182. }
  183. internal override bool RemoveItem(JToken item)
  184. {
  185. throw new JsonException("Cannot add or remove items from {0}.".FormatWith(CultureInfo.InvariantCulture, typeof(JProperty)));
  186. }
  187. internal override void RemoveItemAt(int index)
  188. {
  189. throw new JsonException("Cannot add or remove items from {0}.".FormatWith(CultureInfo.InvariantCulture, typeof(JProperty)));
  190. }
  191. internal override int IndexOfItem(JToken item)
  192. {
  193. return _content.IndexOf(item);
  194. }
  195. internal override void InsertItem(int index, JToken item, bool skipParentCheck)
  196. {
  197. // don't add comments to JProperty
  198. if (item != null && item.Type == JTokenType.Comment)
  199. {
  200. return;
  201. }
  202. if (Value != null)
  203. {
  204. throw new JsonException("{0} cannot have multiple values.".FormatWith(CultureInfo.InvariantCulture, typeof(JProperty)));
  205. }
  206. base.InsertItem(0, item, false);
  207. }
  208. internal override bool ContainsItem(JToken item)
  209. {
  210. return (Value == item);
  211. }
  212. internal override void MergeItem(object content, JsonMergeSettings settings)
  213. {
  214. JToken value = (content as JProperty)?.Value;
  215. if (value != null && value.Type != JTokenType.Null)
  216. {
  217. Value = value;
  218. }
  219. }
  220. internal override void ClearItems()
  221. {
  222. throw new JsonException("Cannot add or remove items from {0}.".FormatWith(CultureInfo.InvariantCulture, typeof(JProperty)));
  223. }
  224. internal override bool DeepEquals(JToken node)
  225. {
  226. return (node is JProperty t && _name == t.Name && ContentsEqual(t));
  227. }
  228. internal override JToken CloneToken()
  229. {
  230. return new JProperty(this);
  231. }
  232. /// <summary>
  233. /// Gets the node type for this <see cref="JToken"/>.
  234. /// </summary>
  235. /// <value>The type.</value>
  236. public override JTokenType Type
  237. {
  238. [DebuggerStepThrough]
  239. get { return JTokenType.Property; }
  240. }
  241. internal JProperty(string name)
  242. {
  243. // called from JTokenWriter
  244. ValidationUtils.ArgumentNotNull(name, nameof(name));
  245. _name = name;
  246. }
  247. /// <summary>
  248. /// Initializes a new instance of the <see cref="JProperty"/> class.
  249. /// </summary>
  250. /// <param name="name">The property name.</param>
  251. /// <param name="content">The property content.</param>
  252. public JProperty(string name, params object[] content)
  253. : this(name, (object)content)
  254. {
  255. }
  256. /// <summary>
  257. /// Initializes a new instance of the <see cref="JProperty"/> class.
  258. /// </summary>
  259. /// <param name="name">The property name.</param>
  260. /// <param name="content">The property content.</param>
  261. public JProperty(string name, object content)
  262. {
  263. ValidationUtils.ArgumentNotNull(name, nameof(name));
  264. _name = name;
  265. Value = IsMultiContent(content)
  266. ? new JArray(content)
  267. : CreateFromContent(content);
  268. }
  269. /// <summary>
  270. /// Writes this token to a <see cref="JsonWriter"/>.
  271. /// </summary>
  272. /// <param name="writer">A <see cref="JsonWriter"/> into which this method will write.</param>
  273. /// <param name="converters">A collection of <see cref="JsonConverter"/> which will be used when writing the token.</param>
  274. public override void WriteTo(JsonWriter writer, params JsonConverter[] converters)
  275. {
  276. writer.WritePropertyName(_name);
  277. JToken value = Value;
  278. if (value != null)
  279. {
  280. value.WriteTo(writer, converters);
  281. }
  282. else
  283. {
  284. writer.WriteNull();
  285. }
  286. }
  287. internal override int GetDeepHashCode()
  288. {
  289. return _name.GetHashCode() ^ (Value?.GetDeepHashCode() ?? 0);
  290. }
  291. /// <summary>
  292. /// Loads a <see cref="JProperty"/> from a <see cref="JsonReader"/>.
  293. /// </summary>
  294. /// <param name="reader">A <see cref="JsonReader"/> that will be read for the content of the <see cref="JProperty"/>.</param>
  295. /// <returns>A <see cref="JProperty"/> that contains the JSON that was read from the specified <see cref="JsonReader"/>.</returns>
  296. public new static JProperty Load(JsonReader reader)
  297. {
  298. return Load(reader, null);
  299. }
  300. /// <summary>
  301. /// Loads a <see cref="JProperty"/> from a <see cref="JsonReader"/>.
  302. /// </summary>
  303. /// <param name="reader">A <see cref="JsonReader"/> that will be read for the content of the <see cref="JProperty"/>.</param>
  304. /// <param name="settings">The <see cref="JsonLoadSettings"/> used to load the JSON.
  305. /// If this is <c>null</c>, default load settings will be used.</param>
  306. /// <returns>A <see cref="JProperty"/> that contains the JSON that was read from the specified <see cref="JsonReader"/>.</returns>
  307. public new static JProperty Load(JsonReader reader, JsonLoadSettings settings)
  308. {
  309. if (reader.TokenType == JsonToken.None)
  310. {
  311. if (!reader.Read())
  312. {
  313. throw JsonReaderException.Create(reader, "Error reading JProperty from JsonReader.");
  314. }
  315. }
  316. reader.MoveToContent();
  317. if (reader.TokenType != JsonToken.PropertyName)
  318. {
  319. throw JsonReaderException.Create(reader, "Error reading JProperty from JsonReader. Current JsonReader item is not a property: {0}".FormatWith(CultureInfo.InvariantCulture, reader.TokenType));
  320. }
  321. JProperty p = new JProperty((string)reader.Value);
  322. p.SetLineInfo(reader as IJsonLineInfo, settings);
  323. p.ReadTokenFrom(reader, settings);
  324. return p;
  325. }
  326. }
  327. }