PageRenderTime 46ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/Raven.Abstractions/Json/JsonMultiDimensionalArrayConverter.cs

https://github.com/jalchr/ravendb
C# | 197 lines | 97 code | 30 blank | 70 comment | 21 complexity | b420c3d8241ef16a611a886e7a7983f3 MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using Raven.Imports.Newtonsoft.Json;
  6. using System.Collections;
  7. namespace Raven.Abstractions.Json
  8. {
  9. /// <summary>
  10. /// Convert a MultiDimensional Array to a json string
  11. /// </summary>
  12. public class JsonMultiDimensionalArrayConverter : JsonConverter
  13. {
  14. /// <summary>
  15. /// Determines whether this instance can convert the specified object type.
  16. /// </summary>
  17. /// <param name="objectType">Type of the object.</param>
  18. /// <returns>
  19. /// <c>true</c> if this instance can convert the specified object type; otherwise, <c>false</c>.
  20. /// </returns>
  21. public override bool CanConvert(Type objectType)
  22. {
  23. return objectType.IsArray && objectType.GetArrayRank() > 1;
  24. }
  25. /// <summary>
  26. /// Reads the JSON representation of the object.
  27. /// </summary>
  28. /// <param name="reader">The <see cref="T:Raven.Imports.Newtonsoft.Json.JsonReader"/> to read from.</param>
  29. /// <param name="objectType">Type of the object.</param>
  30. /// <param name="existingValue">The existing value of object being read.</param>
  31. /// <param name="serializer">The calling serializer.</param>
  32. /// <returns>The object value.</returns>
  33. public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
  34. {
  35. // Handle null values
  36. if (reader.TokenType == JsonToken.Null) return null;
  37. var arrayItemType = objectType.GetElementType();
  38. var arrayRank = objectType.GetArrayRank();
  39. // Retrieve all the values from the Json
  40. var arrayValues = ReadRank(reader, serializer, arrayItemType);
  41. // Determine the lengths of all ranks for the array
  42. var rankLengthList = GetRankLengthList(arrayValues);
  43. // If empty values were found, make sure the ranks match in size
  44. for (var i = rankLengthList.Count; i < arrayRank; i++)
  45. {
  46. rankLengthList.Add(0);
  47. }
  48. var rankLengthArray = rankLengthList.ToArray();
  49. // Create the array that will hold the values
  50. var retVal = Array.CreateInstance(arrayItemType, rankLengthArray);
  51. // Make the assignments
  52. SetValues(retVal, rankLengthArray, new int[0], 0, arrayValues);
  53. return retVal;
  54. }
  55. /// <summary>
  56. /// Writes the JSON representation of the object.
  57. /// </summary>
  58. /// <param name="writer">The <see cref="T:Raven.Imports.Newtonsoft.Json.JsonWriter"/> to write to.</param>
  59. /// <param name="value">The value.</param>
  60. /// <param name="serializer">The calling serializer.</param>
  61. public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
  62. {
  63. // Write out the array to Json
  64. WriteRank(writer, serializer, value as Array, 0, new int[0]);
  65. }
  66. #region Helpers
  67. /// <summary>
  68. /// Read in all the values from the Json reader and populate a nested ArrayList
  69. /// </summary>
  70. /// <param name="reader">JsonReader to use</param>
  71. /// <param name="serializer">JsonSerializer to use</param>
  72. /// <param name="elementType">The element type</param>
  73. /// <returns></returns>
  74. private List<object> ReadRank(JsonReader reader, JsonSerializer serializer, Type elementType)
  75. {
  76. var retVal = new List<object>();
  77. reader.Read();
  78. while (reader.TokenType != JsonToken.EndArray && reader.TokenType != JsonToken.Null && reader.TokenType != JsonToken.EndObject)
  79. {
  80. // If another array is found, it is a new rank
  81. // Otherwise, we have a value
  82. if (reader.TokenType == JsonToken.StartArray)
  83. retVal.Add(ReadRank(reader, serializer, elementType));
  84. else
  85. retVal.Add(serializer.Deserialize(reader, elementType));
  86. reader.Read();
  87. }
  88. return retVal;
  89. }
  90. /// <summary>
  91. /// Retrieve a list of lengths for each rank represented
  92. /// </summary>
  93. /// <param name="arrayList">The list to process</param>
  94. /// <returns></returns>
  95. private List<int> GetRankLengthList(List<object> arrayList)
  96. {
  97. var retVal = new List<int>();
  98. retVal.Add(arrayList.Count);
  99. if (arrayList.Count > 0)
  100. {
  101. var childArrayList = arrayList[0] as List<object>;
  102. // If there are more children arrays, there are more ranks
  103. // Retrieve them and add to results
  104. if (childArrayList != null && childArrayList.Count > 0)
  105. retVal.AddRange(GetRankLengthList(childArrayList));
  106. }
  107. return retVal;
  108. }
  109. /// <summary>
  110. /// Assign values from the ArrayList into their respective place in the multidimensional array
  111. /// </summary>
  112. /// <param name="multiDimensionalArray">Array that will be receiving the newValues</param>
  113. /// <param name="rankLengthList">A list of the lengths of each rank</param>
  114. /// <param name="assignToIndexList">A list of the current index settings to be used for assignments</param>
  115. /// <param name="currentRank">Rank currently being processed</param>
  116. /// <param name="newValues">New Values that will be used in the assignment</param>
  117. private void SetValues(Array multiDimensionalArray, int[] rankLengthList, int[] assignToIndexList, int currentRank, List<object> newValues)
  118. {
  119. // Lengthen the assignToIndex and persist the values.
  120. var myAssignToIndex = assignToIndexList.GetUpperBound(0) + 1;
  121. var myAssignToIndexList = new int[myAssignToIndex + 1];
  122. Array.Copy(assignToIndexList, myAssignToIndexList, assignToIndexList.Length);
  123. // Loop through all values in the current rank for processing
  124. var currentRankLength = rankLengthList[currentRank];
  125. for (var i = 0; i < currentRankLength; i++)
  126. {
  127. // Assign currentIndex to the list of assignmentIndexes
  128. myAssignToIndexList[myAssignToIndex] = i;
  129. // If more ranks are found, process them
  130. // Otherwise, we have values, so make the assignment
  131. if (currentRank < multiDimensionalArray.Rank - 1)
  132. SetValues(multiDimensionalArray, rankLengthList, myAssignToIndexList, currentRank + 1, newValues[i] as List<object>);
  133. else
  134. multiDimensionalArray.SetValue(newValues[i], myAssignToIndexList);
  135. }
  136. }
  137. /// <summary>
  138. /// Write a rank of an array in Json format
  139. /// </summary>
  140. /// <param name="writer">JsonWriter in use</param>
  141. /// <param name="serializer">JsonSerializer in use</param>
  142. /// <param name="array">Array to be written</param>
  143. /// <param name="currentRank">Current rank "depth"</param>
  144. /// <param name="assignFromIndexList">List of indexes currently being used to read from the array</param>
  145. private void WriteRank(JsonWriter writer, JsonSerializer serializer, Array array, int currentRank, int[] assignFromIndexList)
  146. {
  147. writer.WriteStartArray();
  148. var lb = array.GetLowerBound(currentRank);
  149. var ub = array.GetUpperBound(currentRank);
  150. // Create a new indices list (one bigger than passed in) and fill with existing values
  151. var myAssignFromIndex = assignFromIndexList.GetUpperBound(0) + 1;
  152. var myAssignFromIndexList = new int[myAssignFromIndex + 1];
  153. Array.Copy(assignFromIndexList, myAssignFromIndexList, assignFromIndexList.Length);
  154. for (var i = lb; i <= ub; i++)
  155. {
  156. // set current index of this current rank
  157. myAssignFromIndexList[myAssignFromIndex] = i;
  158. if (currentRank < array.Rank - 1) // There are still more ranks, process them
  159. WriteRank(writer, serializer, array, currentRank + 1, myAssignFromIndexList);
  160. else // This is the "bottom" rank, write out values
  161. serializer.Serialize(writer, array.GetValue(myAssignFromIndexList));
  162. }
  163. writer.WriteEndArray();
  164. }
  165. #endregion
  166. }
  167. }