2
// Copyright (c) 2007 James Newton-King
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
13
// The above copyright notice and this permission notice shall be
14
// included in all copies or substantial portions of the Software.
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.
27
using System.Collections.Generic;
28
using System.Collections.ObjectModel;
30
using System.Collections.Specialized;
32
using System.ComponentModel;
33
#if !(NET35 || NET20 || WINDOWS_PHONE || PORTABLE)
35
using System.Linq.Expressions;
38
using Newtonsoft.Json.Utilities;
39
using System.Globalization;
41
using Newtonsoft.Json.Utilities.LinqBridge;
46
namespace Newtonsoft.Json.Linq
49
/// Represents a JSON object.
52
/// <code lang="cs" source="..\Src\Newtonsoft.Json.Tests\Documentation\LinqToJsonTests.cs" region="LinqToJsonCreateParse" title="Parsing a JSON Object from Text" />
54
public class JObject : JContainer, IDictionary<string, JToken>, INotifyPropertyChanged
55
#if !(SILVERLIGHT || NETFX_CORE || PORTABLE)
56
, ICustomTypeDescriptor
58
#if !(SILVERLIGHT || NET20 || NETFX_CORE || PORTABLE)
59
, INotifyPropertyChanging
62
private readonly JPropertyKeyedCollection _properties = new JPropertyKeyedCollection();
65
/// Gets the container's children tokens.
67
/// <value>The container's children tokens.</value>
68
protected override IList<JToken> ChildrenTokens
70
get { return _properties; }
74
/// Occurs when a property value changes.
76
public event PropertyChangedEventHandler PropertyChanged;
78
#if !(SILVERLIGHT || NET20 || NETFX_CORE || PORTABLE)
80
/// Occurs when a property value is changing.
82
public event PropertyChangingEventHandler PropertyChanging;
86
/// Initializes a new instance of the <see cref="JObject"/> class.
93
/// Initializes a new instance of the <see cref="JObject"/> class from another <see cref="JObject"/> object.
95
/// <param name="other">A <see cref="JObject"/> object to copy from.</param>
96
public JObject(JObject other)
102
/// Initializes a new instance of the <see cref="JObject"/> class with the specified content.
104
/// <param name="content">The contents of the object.</param>
105
public JObject(params object[] content)
106
: this((object)content)
111
/// Initializes a new instance of the <see cref="JObject"/> class with the specified content.
113
/// <param name="content">The contents of the object.</param>
114
public JObject(object content)
119
internal override bool DeepEquals(JToken node)
121
JObject t = node as JObject;
125
return _properties.Compare(t._properties);
128
internal override void InsertItem(int index, JToken item, bool skipParentCheck)
130
// don't add comments to JObject, no name to reference comment by
131
if (item != null && item.Type == JTokenType.Comment)
134
base.InsertItem(index, item, skipParentCheck);
137
internal override void ValidateToken(JToken o, JToken existing)
139
ValidationUtils.ArgumentNotNull(o, "o");
141
if (o.Type != JTokenType.Property)
142
throw new ArgumentException("Can not add {0} to {1}.".FormatWith(CultureInfo.InvariantCulture, o.GetType(), GetType()));
144
JProperty newProperty = (JProperty) o;
146
if (existing != null)
148
JProperty existingProperty = (JProperty) existing;
150
if (newProperty.Name == existingProperty.Name)
154
if (_properties.TryGetValue(newProperty.Name, out existing))
155
throw new ArgumentException("Can not add property {0} to {1}. Property with the same name already exists on object.".FormatWith(CultureInfo.InvariantCulture, newProperty.Name, GetType()));
158
internal void InternalPropertyChanged(JProperty childProperty)
160
OnPropertyChanged(childProperty.Name);
161
#if !(SILVERLIGHT || NETFX_CORE || PORTABLE)
162
if (_listChanged != null)
163
OnListChanged(new ListChangedEventArgs(ListChangedType.ItemChanged, IndexOfItem(childProperty)));
165
#if SILVERLIGHT || !(NET20 || NET35 || PORTABLE)
166
if (_collectionChanged != null)
167
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, childProperty, childProperty, IndexOfItem(childProperty)));
171
internal void InternalPropertyChanging(JProperty childProperty)
173
#if !(SILVERLIGHT || NET20 || NETFX_CORE || PORTABLE)
174
OnPropertyChanging(childProperty.Name);
178
internal override JToken CloneToken()
180
return new JObject(this);
184
/// Gets the node type for this <see cref="JToken"/>.
186
/// <value>The type.</value>
187
public override JTokenType Type
189
get { return JTokenType.Object; }
193
/// Gets an <see cref="IEnumerable{JProperty}"/> of this object's properties.
195
/// <returns>An <see cref="IEnumerable{JProperty}"/> of this object's properties.</returns>
196
public IEnumerable<JProperty> Properties()
198
return _properties.Cast<JProperty>();
202
/// Gets a <see cref="JProperty"/> the specified name.
204
/// <param name="name">The property name.</param>
205
/// <returns>A <see cref="JProperty"/> with the specified name or null.</returns>
206
public JProperty Property(string name)
212
_properties.TryGetValue(name, out property);
213
return (JProperty)property;
217
/// Gets an <see cref="JEnumerable{JToken}"/> of this object's property values.
219
/// <returns>An <see cref="JEnumerable{JToken}"/> of this object's property values.</returns>
220
public JEnumerable<JToken> PropertyValues()
222
return new JEnumerable<JToken>(Properties().Select(p => p.Value));
226
/// Gets the <see cref="JToken"/> with the specified key.
228
/// <value>The <see cref="JToken"/> with the specified key.</value>
229
public override JToken this[object key]
233
ValidationUtils.ArgumentNotNull(key, "o");
235
string propertyName = key as string;
236
if (propertyName == null)
237
throw new ArgumentException("Accessed JObject values with invalid key value: {0}. Object property name expected.".FormatWith(CultureInfo.InvariantCulture, MiscellaneousUtils.ToString(key)));
239
return this[propertyName];
243
ValidationUtils.ArgumentNotNull(key, "o");
245
string propertyName = key as string;
246
if (propertyName == null)
247
throw new ArgumentException("Set JObject values with invalid key value: {0}. Object property name expected.".FormatWith(CultureInfo.InvariantCulture, MiscellaneousUtils.ToString(key)));
249
this[propertyName] = value;
254
/// Gets or sets the <see cref="Newtonsoft.Json.Linq.JToken"/> with the specified property name.
257
public JToken this[string propertyName]
261
ValidationUtils.ArgumentNotNull(propertyName, "propertyName");
263
JProperty property = Property(propertyName);
265
return (property != null) ? property.Value : null;
269
JProperty property = Property(propertyName);
270
if (property != null)
272
property.Value = value;
276
#if !(SILVERLIGHT || NET20 || NETFX_CORE || PORTABLE)
277
OnPropertyChanging(propertyName);
279
Add(new JProperty(propertyName, value));
280
OnPropertyChanged(propertyName);
286
/// Loads an <see cref="JObject"/> from a <see cref="JsonReader"/>.
288
/// <param name="reader">A <see cref="JsonReader"/> that will be read for the content of the <see cref="JObject"/>.</param>
289
/// <returns>A <see cref="JObject"/> that contains the JSON that was read from the specified <see cref="JsonReader"/>.</returns>
290
public static new JObject Load(JsonReader reader)
292
ValidationUtils.ArgumentNotNull(reader, "reader");
294
if (reader.TokenType == JsonToken.None)
297
throw JsonReaderException.Create(reader, "Error reading JObject from JsonReader.");
300
while (reader.TokenType == JsonToken.Comment)
305
if (reader.TokenType != JsonToken.StartObject)
307
throw JsonReaderException.Create(reader, "Error reading JObject from JsonReader. Current JsonReader item is not an object: {0}".FormatWith(CultureInfo.InvariantCulture, reader.TokenType));
310
JObject o = new JObject();
311
o.SetLineInfo(reader as IJsonLineInfo);
313
o.ReadTokenFrom(reader);
319
/// Load a <see cref="JObject"/> from a string that contains JSON.
321
/// <param name="json">A <see cref="String"/> that contains JSON.</param>
322
/// <returns>A <see cref="JObject"/> populated from the string that contains JSON.</returns>
324
/// <code lang="cs" source="..\Src\Newtonsoft.Json.Tests\Documentation\LinqToJsonTests.cs" region="LinqToJsonCreateParse" title="Parsing a JSON Object from Text" />
326
public static new JObject Parse(string json)
328
JsonReader reader = new JsonTextReader(new StringReader(json));
330
JObject o = Load(reader);
332
if (reader.Read() && reader.TokenType != JsonToken.Comment)
333
throw JsonReaderException.Create(reader, "Additional text found in JSON string after parsing content.");
339
/// Creates a <see cref="JObject"/> from an object.
341
/// <param name="o">The object that will be used to create <see cref="JObject"/>.</param>
342
/// <returns>A <see cref="JObject"/> with the values of the specified object</returns>
343
public static new JObject FromObject(object o)
345
return FromObject(o, new JsonSerializer());
349
/// Creates a <see cref="JArray"/> from an object.
351
/// <param name="o">The object that will be used to create <see cref="JArray"/>.</param>
352
/// <param name="jsonSerializer">The <see cref="JsonSerializer"/> that will be used to read the object.</param>
353
/// <returns>A <see cref="JArray"/> with the values of the specified object</returns>
354
public static new JObject FromObject(object o, JsonSerializer jsonSerializer)
356
JToken token = FromObjectInternal(o, jsonSerializer);
358
if (token != null && token.Type != JTokenType.Object)
359
throw new ArgumentException("Object serialized to {0}. JObject instance expected.".FormatWith(CultureInfo.InvariantCulture, token.Type));
361
return (JObject)token;
365
/// Writes this token to a <see cref="JsonWriter"/>.
367
/// <param name="writer">A <see cref="JsonWriter"/> into which this method will write.</param>
368
/// <param name="converters">A collection of <see cref="JsonConverter"/> which will be used when writing the token.</param>
369
public override void WriteTo(JsonWriter writer, params JsonConverter[] converters)
371
writer.WriteStartObject();
373
for (int i = 0; i < _properties.Count; i++)
375
_properties[i].WriteTo(writer, converters);
378
writer.WriteEndObject();
382
/// Gets the <see cref="Newtonsoft.Json.Linq.JToken"/> with the specified property name.
384
/// <param name="propertyName">Name of the property.</param>
385
/// <value>The <see cref="Newtonsoft.Json.Linq.JToken"/> with the specified property name.</value>
386
public JToken GetValue(string propertyName)
388
return GetValue(propertyName, StringComparison.Ordinal);
392
/// Gets the <see cref="Newtonsoft.Json.Linq.JToken"/> with the specified property name.
393
/// The exact property name will be searched for first and if no matching property is found then
394
/// the <see cref="StringComparison"/> will be used to match a property.
396
/// <param name="propertyName">Name of the property.</param>
397
/// <param name="comparison">One of the enumeration values that specifies how the strings will be compared.</param>
398
/// <value>The <see cref="Newtonsoft.Json.Linq.JToken"/> with the specified property name.</value>
399
public JToken GetValue(string propertyName, StringComparison comparison)
401
if (propertyName == null)
404
// attempt to get value via dictionary first for performance
405
JProperty property = Property(propertyName);
406
if (property != null)
407
return property.Value;
409
// test above already uses this comparison so no need to repeat
410
if (comparison != StringComparison.Ordinal)
412
foreach (JProperty p in _properties)
414
if (string.Equals(p.Name, propertyName, comparison))
423
/// Tries to get the <see cref="Newtonsoft.Json.Linq.JToken"/> with the specified property name.
424
/// The exact property name will be searched for first and if no matching property is found then
425
/// the <see cref="StringComparison"/> will be used to match a property.
427
/// <param name="propertyName">Name of the property.</param>
428
/// <param name="value">The value.</param>
429
/// <param name="comparison">One of the enumeration values that specifies how the strings will be compared.</param>
430
/// <returns>true if a value was successfully retrieved; otherwise, false.</returns>
431
public bool TryGetValue(string propertyName, StringComparison comparison, out JToken value)
433
value = GetValue(propertyName, comparison);
434
return (value != null);
437
#region IDictionary<string,JToken> Members
439
/// Adds the specified property name.
441
/// <param name="propertyName">Name of the property.</param>
442
/// <param name="value">The value.</param>
443
public void Add(string propertyName, JToken value)
445
Add(new JProperty(propertyName, value));
448
bool IDictionary<string, JToken>.ContainsKey(string key)
450
return _properties.Contains(key);
453
ICollection<string> IDictionary<string, JToken>.Keys
455
// todo: make order the collection returned match JObject order
456
get { return _properties.Keys; }
460
/// Removes the property with the specified name.
462
/// <param name="propertyName">Name of the property.</param>
463
/// <returns>true if item was successfully removed; otherwise, false.</returns>
464
public bool Remove(string propertyName)
466
JProperty property = Property(propertyName);
467
if (property == null)
475
/// Tries the get value.
477
/// <param name="propertyName">Name of the property.</param>
478
/// <param name="value">The value.</param>
479
/// <returns>true if a value was successfully retrieved; otherwise, false.</returns>
480
public bool TryGetValue(string propertyName, out JToken value)
482
JProperty property = Property(propertyName);
483
if (property == null)
489
value = property.Value;
493
ICollection<JToken> IDictionary<string, JToken>.Values
497
// todo: need to wrap _properties.Values with a collection to get the JProperty value
498
throw new NotImplementedException();
504
#region ICollection<KeyValuePair<string,JToken>> Members
506
void ICollection<KeyValuePair<string,JToken>>.Add(KeyValuePair<string, JToken> item)
508
Add(new JProperty(item.Key, item.Value));
511
void ICollection<KeyValuePair<string, JToken>>.Clear()
516
bool ICollection<KeyValuePair<string,JToken>>.Contains(KeyValuePair<string, JToken> item)
518
JProperty property = Property(item.Key);
519
if (property == null)
522
return (property.Value == item.Value);
525
void ICollection<KeyValuePair<string,JToken>>.CopyTo(KeyValuePair<string, JToken>[] array, int arrayIndex)
528
throw new ArgumentNullException("array");
530
throw new ArgumentOutOfRangeException("arrayIndex", "arrayIndex is less than 0.");
531
if (arrayIndex >= array.Length && arrayIndex != 0)
532
throw new ArgumentException("arrayIndex is equal to or greater than the length of array.");
533
if (Count > array.Length - arrayIndex)
534
throw new ArgumentException("The number of elements in the source JObject is greater than the available space from arrayIndex to the end of the destination array.");
537
foreach (JProperty property in _properties)
539
array[arrayIndex + index] = new KeyValuePair<string, JToken>(property.Name, property.Value);
544
bool ICollection<KeyValuePair<string,JToken>>.IsReadOnly
546
get { return false; }
549
bool ICollection<KeyValuePair<string,JToken>>.Remove(KeyValuePair<string, JToken> item)
551
if (!((ICollection<KeyValuePair<string,JToken>>)this).Contains(item))
554
((IDictionary<string, JToken>)this).Remove(item.Key);
560
internal override int GetDeepHashCode()
562
return ContentsHashCode();
566
/// Returns an enumerator that iterates through the collection.
569
/// A <see cref="T:System.Collections.Generic.IEnumerator`1"/> that can be used to iterate through the collection.
571
public IEnumerator<KeyValuePair<string, JToken>> GetEnumerator()
573
foreach (JProperty property in _properties)
575
yield return new KeyValuePair<string, JToken>(property.Name, property.Value);
580
/// Raises the <see cref="PropertyChanged"/> event with the provided arguments.
582
/// <param name="propertyName">Name of the property.</param>
583
protected virtual void OnPropertyChanged(string propertyName)
585
if (PropertyChanged != null)
586
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
589
#if !(SILVERLIGHT || NETFX_CORE || PORTABLE || NET20)
591
/// Raises the <see cref="PropertyChanging"/> event with the provided arguments.
593
/// <param name="propertyName">Name of the property.</param>
594
protected virtual void OnPropertyChanging(string propertyName)
596
if (PropertyChanging != null)
597
PropertyChanging(this, new PropertyChangingEventArgs(propertyName));
601
#if !(SILVERLIGHT || NETFX_CORE || PORTABLE)
602
// include custom type descriptor on JObject rather than use a provider because the properties are specific to a type
603
#region ICustomTypeDescriptor
605
/// Returns the properties for this instance of a component.
608
/// A <see cref="T:System.ComponentModel.PropertyDescriptorCollection"/> that represents the properties for this component instance.
610
PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties()
612
return ((ICustomTypeDescriptor) this).GetProperties(null);
615
private static Type GetTokenPropertyType(JToken token)
619
JValue v = (JValue)token;
620
return (v.Value != null) ? v.Value.GetType() : typeof(object);
623
return token.GetType();
627
/// Returns the properties for this instance of a component using the attribute array as a filter.
629
/// <param name="attributes">An array of type <see cref="T:System.Attribute"/> that is used as a filter.</param>
631
/// A <see cref="T:System.ComponentModel.PropertyDescriptorCollection"/> that represents the filtered properties for this component instance.
633
PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes)
635
PropertyDescriptorCollection descriptors = new PropertyDescriptorCollection(null);
637
foreach (KeyValuePair<string, JToken> propertyValue in this)
639
descriptors.Add(new JPropertyDescriptor(propertyValue.Key, GetTokenPropertyType(propertyValue.Value)));
646
/// Returns a collection of custom attributes for this instance of a component.
649
/// An <see cref="T:System.ComponentModel.AttributeCollection"/> containing the attributes for this object.
651
AttributeCollection ICustomTypeDescriptor.GetAttributes()
653
return AttributeCollection.Empty;
657
/// Returns the class name of this instance of a component.
660
/// The class name of the object, or null if the class does not have a name.
662
string ICustomTypeDescriptor.GetClassName()
668
/// Returns the name of this instance of a component.
671
/// The name of the object, or null if the object does not have a name.
673
string ICustomTypeDescriptor.GetComponentName()
679
/// Returns a type converter for this instance of a component.
682
/// A <see cref="T:System.ComponentModel.TypeConverter"/> that is the converter for this object, or null if there is no <see cref="T:System.ComponentModel.TypeConverter"/> for this object.
684
TypeConverter ICustomTypeDescriptor.GetConverter()
686
return new TypeConverter();
690
/// Returns the default event for this instance of a component.
693
/// An <see cref="T:System.ComponentModel.EventDescriptor"/> that represents the default event for this object, or null if this object does not have events.
695
EventDescriptor ICustomTypeDescriptor.GetDefaultEvent()
701
/// Returns the default property for this instance of a component.
704
/// A <see cref="T:System.ComponentModel.PropertyDescriptor"/> that represents the default property for this object, or null if this object does not have properties.
706
PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty()
712
/// Returns an editor of the specified type for this instance of a component.
714
/// <param name="editorBaseType">A <see cref="T:System.Type"/> that represents the editor for this object.</param>
716
/// An <see cref="T:System.Object"/> of the specified type that is the editor for this object, or null if the editor cannot be found.
718
object ICustomTypeDescriptor.GetEditor(Type editorBaseType)
724
/// Returns the events for this instance of a component using the specified attribute array as a filter.
726
/// <param name="attributes">An array of type <see cref="T:System.Attribute"/> that is used as a filter.</param>
728
/// An <see cref="T:System.ComponentModel.EventDescriptorCollection"/> that represents the filtered events for this component instance.
730
EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes)
732
return EventDescriptorCollection.Empty;
736
/// Returns the events for this instance of a component.
739
/// An <see cref="T:System.ComponentModel.EventDescriptorCollection"/> that represents the events for this component instance.
741
EventDescriptorCollection ICustomTypeDescriptor.GetEvents()
743
return EventDescriptorCollection.Empty;
747
/// Returns an object that contains the property described by the specified property descriptor.
749
/// <param name="pd">A <see cref="T:System.ComponentModel.PropertyDescriptor"/> that represents the property whose owner is to be found.</param>
751
/// An <see cref="T:System.Object"/> that represents the owner of the specified property.
753
object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd)
760
#if !(NET35 || NET20 || WINDOWS_PHONE || PORTABLE)
762
/// Returns the <see cref="T:System.Dynamic.DynamicMetaObject"/> responsible for binding operations performed on this object.
764
/// <param name="parameter">The expression tree representation of the runtime value.</param>
766
/// The <see cref="T:System.Dynamic.DynamicMetaObject"/> to bind this object.
768
protected override DynamicMetaObject GetMetaObject(Expression parameter)
770
return new DynamicProxyMetaObject<JObject>(parameter, this, new JObjectDynamicProxy(), true);
773
private class JObjectDynamicProxy : DynamicProxy<JObject>
775
public override bool TryGetMember(JObject instance, GetMemberBinder binder, out object result)
777
// result can be null
778
result = instance[binder.Name];
782
public override bool TrySetMember(JObject instance, SetMemberBinder binder, object value)
784
JToken v = value as JToken;
786
// this can throw an error if value isn't a valid for a JValue
788
v = new JValue(value);
790
instance[binder.Name] = v;
794
public override IEnumerable<string> GetDynamicMemberNames(JObject instance)
796
return instance.Properties().Select(p => p.Name);
b'\\ No newline at end of file'