/* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. * A copy of the License is located at * * http://aws.amazon.com/apache2.0 * * or in the "license" file accompanying this file. This file is distributed * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. See the License for the specific language governing * permissions and limitations under the License. */ using System; using System.Collections.Generic; using System.Globalization; using System.Text; using Amazon.DynamoDBv2.Model; using Amazon.Util; using ThirdParty.Json.LitJson; using System.IO; namespace Amazon.DynamoDBv2.DocumentModel { /// /// Utility methods to handle conversion from/to JSON /// internal static class JsonUtils { /// /// Parses JSON text to produce Document. /// /// /// public static Document FromJson(string jsonText) { var json = JsonMapper.ToObject(jsonText); if (!json.IsObject) throw new InvalidOperationException("Expected object at JSON root."); var document = ToEntry(json, DynamoDBEntryConversion.V2) as Document; if (document == null) throw new InvalidOperationException(); return document; } /// /// Parses JSON text to produce an of type . /// /// /// An of type public static IEnumerable FromJsonArray(string jsonText) { var json = JsonMapper.ToObject(jsonText); if (!json.IsArray) throw new InvalidOperationException("Expected array at JSON root."); var array = new List(); for(int i=0;i /// Creates JSON text for a given Document /// /// /// /// public static string ToJson(Document document, bool prettyPrint) { var sb = new StringBuilder(); var writer = new JsonWriter(sb); writer.PrettyPrint = prettyPrint; WriteJson(document, writer, DynamoDBEntryConversion.V2); // Trim everything before the first '{' character var jsonIndex = FirstIndex(sb, '{'); if (jsonIndex > 0) sb.Remove(0, jsonIndex); var jsonText = sb.ToString(); return jsonText; } /// /// Decodes specific attributes from base64 to their binary representation. /// /// /// public static void DecodeBase64Attributes(Document document, params string[] attributeNames) { if (attributeNames == null || attributeNames.Length == 0) return; var decodedValues = new Dictionary(StringComparer.Ordinal); // Convert, but don't alter the original yet foreach(var attributeName in attributeNames) { DynamoDBEntry entry; // If an attribute is not present, do nothing if (!document.TryGetValue(attributeName, out entry)) continue; DynamoDBEntry decodedEntry; if (!TryDecodeBase64(entry, out decodedEntry)) throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Unable to decode attribute {0} of type {1} in document", attributeName, entry.GetType().FullName)); decodedValues[attributeName] = decodedEntry; } // Update document with decoded attribute values foreach(var kvp in decodedValues) { var attributeName = kvp.Key; var decodedEntry = kvp.Value; document[attributeName] = decodedEntry; } } // Attempts to decode a particular DynamoDBEntry. // May throw exceptions, in particular if the data is not base64 encoded private static bool TryDecodeBase64(DynamoDBEntry entry, out DynamoDBEntry decodedEntry) { decodedEntry = null; // Convert string primitive (S) to binary primitive (B) var primitive = entry as Primitive; if (primitive != null && primitive.Type == DynamoDBEntryType.String) { // Decode the contents var base64 = primitive.Value as string; byte[] bytes; if (!TryDecodeBase64(base64, out bytes)) return false; // Store as binary primitive (B) decodedEntry = new Primitive(bytes); return true; } // Convert string set (SS) to binary set (BS) var primitiveList = entry as PrimitiveList; if (primitiveList != null && primitiveList.Type == DynamoDBEntryType.String) { var decodedList = new PrimitiveList(DynamoDBEntryType.Binary); foreach(var item in primitiveList.Entries) { // Attempt to decode DynamoDBEntry decodedItem; if (!TryDecodeBase64(item, out decodedItem)) return false; // The decoded item must be a Primitive Primitive decodedPrimitive = decodedItem as Primitive; if (decodedPrimitive == null) return false; decodedList.Add(decodedPrimitive); } decodedEntry = decodedList; return true; } // In a given list (L), convert every string primitive (S) to binary primitive (B) // Non-strings and strings that cannot be converted will be left as-is var dynamoDBList = entry as DynamoDBList; if (dynamoDBList != null) { var decodedList = new DynamoDBList(); foreach(var item in dynamoDBList.Entries) { DynamoDBEntry decodedItem; if (!TryDecodeBase64(item, out decodedItem)) { // if decoding could not succeed, store same item decodedItem = item; } decodedList.Add(decodedItem); } decodedEntry = decodedList; return true; } return false; } // Attempt to decode base64 string to bytes private static bool TryDecodeBase64(string base64Data, out byte[] bytes) { bytes = null; if (base64Data == null) return false; try { bytes = Convert.FromBase64String(base64Data); return true; } catch { return false; } } // Returns a DynamoDB entry for the given JSON data private static DynamoDBEntry ToEntry(JsonData data, DynamoDBEntryConversion conversion) { if (data == null) return new DynamoDBNull(); if (data.IsObject) { var document = new Document(); foreach (var propertyName in data.PropertyNames) { var nestedData = data[propertyName]; var entry = ToEntry(nestedData, conversion); document[propertyName] = entry; } return document; } if (data.IsArray) { var list = new DynamoDBList(); for(int i=0;i