/*
* 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