/// <summary> /// Get an element from a table by its ID. /// </summary> /// <param name="id">The ID of the element.</param> /// <param name="parameters"> /// A dictionary of user-defined parameters and values to include in the request URI query string. /// </param> /// <returns>The desired element as JSON object.</returns> private async Task <JsonObject> GetSingleValueAsync(object id, IDictionary <string, string> parameters) { // Create a query for just this item string query = string.Format( CultureInfo.InvariantCulture, "$filter={0} eq {1}", MobileServiceTableUrlBuilder.IdPropertyName, TypeExtensions.ToODataConstant(id)); // Send the query IJsonValue response = await this.ReadAsync(query, parameters); // Get the first element in the response JsonObject obj = response.AsObject(); if (obj == null) { JsonArray array = response.AsArray(); if (array != null && array.Count > 0) { obj = array.FirstOrDefault().AsObject(); } } if (obj == null) { throw new InvalidOperationException( string.Format( CultureInfo.InvariantCulture, Resources.MobileServiceTables_GetSingleValueAsync_NotSingleObject, (response ?? JsonExtensions.Null()).Stringify())); } return(obj); }
/// <summary> /// Get an element from a table by its ID. /// </summary> /// <param name="id">The ID of the element.</param> /// <param name="parameters"> /// A dictionary of user-defined parameters and values to include in the request URI query string. /// </param> /// <returns>The desired element.</returns> public new async Task <T> LookupAsync(object id, IDictionary <string, string> parameters) { // TODO: At some point in the future this will be involved in our // caching story and relationships across tables via foreign // keys. IJsonValue value = await this.SendLookupAsync(id, parameters); return(MobileServiceTableSerializer.Deserialize <T>(value.AsObject())); }
/// <summary> /// Gets a named value from an object. /// </summary> /// <param name="value"> /// The object to check (though the type is IJsonValue so multiple /// calls to Get can be chained together). /// </param> /// <param name="name">The name of the value to lookup.</param> /// <returns> /// The value associated with the name, or null if the instance isn't /// an object or the name was not found. /// </returns> public static IJsonValue Get(this IJsonValue value, string name) { JsonObject obj = value.AsObject(); IJsonValue val = null; if (obj != null) { obj.TryGetValue(name, out val); } return(val); }
public void AsObject() { IJsonValue value = null; Assert.IsNull(value.AsObject()); Assert.IsNull(JsonExtensions.Null().AsObject()); Assert.IsNull(JsonValue.CreateStringValue("asdfafd").AsObject()); Assert.IsNull(JsonValue.CreateNumberValue(2.0).AsObject()); Assert.IsNull(JsonValue.CreateBooleanValue(true).AsObject()); Assert.IsNull(new JsonArray().AsObject()); JsonObject obj = new JsonObject(); value = obj; Assert.AreEqual(obj, obj.AsObject()); Assert.AreEqual(obj, value); }
/// <summary> /// Patch an object with the values returned by from the server. Given /// that it's possible for the server to change values on an insert or /// update, we want to make sure the client object reflects those /// changes. /// </summary> /// <param name="original">The first instance.</param> /// <param name="updated">The second instance.</param> /// <returns> /// The first instance patched with values from the second. /// </returns> private static IJsonValue Patch(IJsonValue original, IJsonValue updated) { JsonObject originalObj = original.AsObject(); JsonObject updatedObj = updated.AsObject(); if (originalObj != null && updatedObj != null) { foreach (KeyValuePair <string, JsonValue> property in updatedObj.GetPropertyValues()) { originalObj.SetNamedValue(property.Key, property.Value); } // TODO: Should we also delete any fields on the first object // that aren't also on the second object? Is that a scenario // for scripts? } else { Debug.Assert(false, "Patch expects two JSON objects."); } return(original); }
/// <summary> /// Patch an object with the values returned by from the server. Given /// that it's possible for the server to change values on an insert or /// update, we want to make sure the client object reflects those /// changes. /// </summary> /// <param name="original">The first instance.</param> /// <param name="updated">The second instance.</param> /// <returns> /// The first instance patched with values from the second. /// </returns> private static IJsonValue Patch(IJsonValue original, IJsonValue updated) { JsonObject originalObj = original.AsObject(); JsonObject updatedObj = updated.AsObject(); if (originalObj != null && updatedObj != null) { foreach (KeyValuePair<string, JsonValue> property in updatedObj.GetPropertyValues()) { originalObj.SetNamedValue(property.Key, property.Value); } // TODO: Should we also delete any fields on the first object // that aren't also on the second object? Is that a scenario // for scripts? } else { Debug.Assert(false, "Patch expects two JSON objects."); } return original; }
/// <summary> /// Deserialize a JSON value into an instance. /// </summary> /// <param name="value">The JSON value.</param> /// <param name="instance">The instance to deserialize into.</param> /// <param name="ignoreCustomSerialization"> /// A value to indicate whether or not custom serialization should be /// ignored if the instance implements /// ICustomMobileServiceTableSerialization. This flag is used by /// implementations of ICustomMobileServiceTableSerialization that want /// to invoke the default serialization behavior. /// </param> public static void Deserialize(IJsonValue value, object instance, bool ignoreCustomSerialization) { if (value == null) { throw new ArgumentNullException("value"); } else if (instance == null) { throw new ArgumentNullException("instance"); } // If the instance implements // ICustomMobileServiceTableSerialization, allow it to handle its // own deserialization. if (!ignoreCustomSerialization) { ICustomMobileServiceTableSerialization custom = instance as ICustomMobileServiceTableSerialization; if (custom != null) { custom.Deserialize(value); return; } } // Get the Mobile Services specific type info SerializableType type = SerializableType.Get(instance.GetType()); // Get the object to deserialize JsonObject obj = value.AsObject(); if (obj == null) { throw new ArgumentException( string.Format( CultureInfo.InvariantCulture, Resources.MobileServiceTableSerializer_Deserialize_NeedObject, value.ValueType), "value"); } // Create a set of required members that we can remove from as we // process their values from the object. If there's anything left // in the set after processing all of the values, then we weren't // given all of our required properties. HashSet <SerializableMember> requiredMembers = new HashSet <SerializableMember>(type.Members.Values.Where(m => m.IsRequired)); // Walk through all of the members defined in the object and // deserialize them into the instance one at a time foreach (KeyValuePair <string, JsonValue> assignment in obj.GetPropertyValues().OrderBy(a => type.GetMemberOrder(a.Key))) { // Look up the instance member corresponding to the JSON member SerializableMember member = null; if (type.Members.TryGetValue(assignment.Key, out member)) { // Remove the member from the required list (does nothing // if it wasn't present) requiredMembers.Remove(member); // Convert the property value into a CLR value using either // the converter or a standard simple JSON mapping. This // will throw for JSON arrays or objects. Also note that // we'll still run the value returned from the converter // through the ChangeType call below to make writing // converters a little easier (but it should be a no-op // for most folks anyway). object propertyValue = null; if (member.Converter != null) { propertyValue = member.Converter.ConvertFromJson(assignment.Value); } else if (!assignment.Value.TryConvert(out propertyValue)) { throw new ArgumentException( string.Format( CultureInfo.InvariantCulture, Resources.MobileServiceTableSerializer_Deserialize_CannotDeserializeValue, assignment.Value.Stringify(), type.Type.FullName, member.MemberName), "value"); } // Change the type of the value to the desired property // type (mostly to handle things like casting issues) and // set the value on the instance. object convertedValue = TypeExtensions.ChangeType(propertyValue, member.Type); member.SetValue(instance, convertedValue); } } // Ensure we were provided all of the required properties. if (requiredMembers.Count > 0) { throw new SerializationException( string.Format( CultureInfo.InvariantCulture, Resources.MobileServiceTableSerializer_Deserialize_MissingRequired, type.Type.FullName, string.Join(", ", requiredMembers.Select(m => m.Name)))); } }