1 // Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information. 2 3 using System.Collections.Generic; 4 using System.Globalization; 5 using System.IO; 6 using System.Runtime.Serialization.Json; 7 using System.Text; 8 using System.Xml; 9 10 namespace System.Json 11 { 12 internal static class JXmlToJsonValueConverter 13 { 14 internal const string RootElementName = "root"; 15 internal const string ItemElementName = "item"; 16 internal const string TypeAttributeName = "type"; 17 internal const string ArrayAttributeValue = "array"; 18 internal const string BooleanAttributeValue = "boolean"; 19 internal const string NullAttributeValue = "null"; 20 internal const string NumberAttributeValue = "number"; 21 internal const string ObjectAttributeValue = "object"; 22 internal const string StringAttributeValue = "string"; 23 private const string TypeHintAttributeName = "__type"; 24 25 private static readonly char[] _floatingPointChars = new char[] { '.', 'e', 'E' }; 26 JXMLToJsonValue(Stream jsonStream)27 public static JsonValue JXMLToJsonValue(Stream jsonStream) 28 { 29 if (jsonStream == null) 30 { 31 throw new ArgumentNullException("jsonStream"); 32 } 33 34 return JXMLToJsonValue(jsonStream, null); 35 } 36 JXMLToJsonValue(string jsonString)37 public static JsonValue JXMLToJsonValue(string jsonString) 38 { 39 if (jsonString == null) 40 { 41 throw new ArgumentNullException("jsonString"); 42 } 43 44 if (jsonString.Length == 0) 45 { 46 throw new ArgumentException(Properties.Resources.JsonStringCannotBeEmpty, "jsonString"); 47 } 48 49 byte[] jsonBytes = Encoding.UTF8.GetBytes(jsonString); 50 51 return JXMLToJsonValue(null, jsonBytes); 52 } 53 JXMLToJsonValue(XmlDictionaryReader jsonReader)54 public static JsonValue JXMLToJsonValue(XmlDictionaryReader jsonReader) 55 { 56 if (jsonReader == null) 57 { 58 throw new ArgumentNullException("jsonReader"); 59 } 60 61 const string RootObjectName = "RootObject"; 62 Stack<JsonValue> jsonStack = new Stack<JsonValue>(); 63 string nodeType = null; 64 bool isEmptyElement = false; 65 66 JsonValue parent = new JsonObject(); 67 jsonStack.Push(parent); 68 string currentName = RootObjectName; 69 70 try 71 { 72 MoveToRootNode(jsonReader); 73 74 while (jsonStack.Count > 0 && jsonReader.NodeType != XmlNodeType.None) 75 { 76 if (parent is JsonObject && currentName == null) 77 { 78 currentName = GetMemberName(jsonReader); 79 } 80 81 nodeType = jsonReader.GetAttribute(TypeAttributeName) ?? StringAttributeValue; 82 83 if (parent is JsonArray) 84 { 85 // For arrays, the element name has to be "item" 86 if (jsonReader.Name != ItemElementName) 87 { 88 throw new FormatException(Properties.Resources.IncorrectJsonFormat); 89 } 90 } 91 92 switch (nodeType) 93 { 94 case NullAttributeValue: 95 case BooleanAttributeValue: 96 case StringAttributeValue: 97 case NumberAttributeValue: 98 JsonPrimitive jsonPrimitive = ReadPrimitive(nodeType, jsonReader); 99 InsertJsonValue(jsonStack, ref parent, ref currentName, jsonPrimitive, true); 100 break; 101 case ArrayAttributeValue: 102 JsonArray jsonArray = CreateJsonArray(jsonReader, ref isEmptyElement); 103 InsertJsonValue(jsonStack, ref parent, ref currentName, jsonArray, isEmptyElement); 104 break; 105 case ObjectAttributeValue: 106 JsonObject jsonObject = CreateObjectWithTypeHint(jsonReader, ref isEmptyElement); 107 InsertJsonValue(jsonStack, ref parent, ref currentName, jsonObject, isEmptyElement); 108 break; 109 default: 110 throw new FormatException(Properties.Resources.IncorrectJsonFormat); 111 } 112 113 while (jsonReader.NodeType == XmlNodeType.EndElement && jsonStack.Count > 0) 114 { 115 jsonReader.Read(); 116 SkipWhitespace(jsonReader); 117 jsonStack.Pop(); 118 if (jsonStack.Count > 0) 119 { 120 parent = jsonStack.Peek(); 121 } 122 } 123 } 124 } 125 catch (XmlException xmlException) 126 { 127 throw new FormatException(Properties.Resources.IncorrectJsonFormat, xmlException); 128 } 129 130 if (jsonStack.Count != 1) 131 { 132 throw new FormatException(Properties.Resources.IncorrectJsonFormat); 133 } 134 135 return parent[RootObjectName]; 136 } 137 JXMLToJsonValue(Stream jsonStream, byte[] jsonBytes)138 private static JsonValue JXMLToJsonValue(Stream jsonStream, byte[] jsonBytes) 139 { 140 try 141 { 142 using (XmlDictionaryReader jsonReader = 143 jsonStream != null 144 ? JsonReaderWriterFactory.CreateJsonReader(jsonStream, XmlDictionaryReaderQuotas.Max) 145 : JsonReaderWriterFactory.CreateJsonReader(jsonBytes, XmlDictionaryReaderQuotas.Max)) 146 { 147 return JXMLToJsonValue(jsonReader); 148 } 149 } 150 catch (XmlException) 151 { 152 throw new FormatException(Properties.Resources.IncorrectJsonFormat); 153 } 154 } 155 InsertJsonValue(Stack<JsonValue> jsonStack, ref JsonValue parent, ref string currentName, JsonValue jsonValue, bool isEmptyElement)156 private static void InsertJsonValue(Stack<JsonValue> jsonStack, ref JsonValue parent, ref string currentName, JsonValue jsonValue, bool isEmptyElement) 157 { 158 if (parent is JsonArray) 159 { 160 ((JsonArray)parent).Add(jsonValue); 161 } 162 else 163 { 164 if (currentName != null) 165 { 166 ((JsonObject)parent)[currentName] = jsonValue; 167 currentName = null; 168 } 169 } 170 171 if (!isEmptyElement) 172 { 173 jsonStack.Push(jsonValue); 174 parent = jsonValue; 175 } 176 } 177 GetMemberName(XmlDictionaryReader jsonReader)178 private static string GetMemberName(XmlDictionaryReader jsonReader) 179 { 180 string name; 181 if (jsonReader.NamespaceURI == ItemElementName && jsonReader.LocalName == ItemElementName) 182 { 183 // JXML special case for names which aren't valid XML names 184 name = jsonReader.GetAttribute(ItemElementName); 185 186 if (name == null) 187 { 188 throw new FormatException(Properties.Resources.IncorrectJsonFormat); 189 } 190 } 191 else 192 { 193 name = jsonReader.Name; 194 } 195 196 return name; 197 } 198 CreateObjectWithTypeHint(XmlDictionaryReader jsonReader, ref bool isEmptyElement)199 private static JsonObject CreateObjectWithTypeHint(XmlDictionaryReader jsonReader, ref bool isEmptyElement) 200 { 201 JsonObject jsonObject = new JsonObject(); 202 string typeHintAttribute = jsonReader.GetAttribute(TypeHintAttributeName); 203 isEmptyElement = jsonReader.IsEmptyElement; 204 jsonReader.ReadStartElement(); 205 SkipWhitespace(jsonReader); 206 207 if (typeHintAttribute != null) 208 { 209 jsonObject.Add(TypeHintAttributeName, typeHintAttribute); 210 } 211 212 return jsonObject; 213 } 214 CreateJsonArray(XmlDictionaryReader jsonReader, ref bool isEmptyElement)215 private static JsonArray CreateJsonArray(XmlDictionaryReader jsonReader, ref bool isEmptyElement) 216 { 217 JsonArray jsonArray = new JsonArray(); 218 isEmptyElement = jsonReader.IsEmptyElement; 219 jsonReader.ReadStartElement(); 220 SkipWhitespace(jsonReader); 221 return jsonArray; 222 } 223 MoveToRootNode(XmlDictionaryReader jsonReader)224 private static void MoveToRootNode(XmlDictionaryReader jsonReader) 225 { 226 while (!jsonReader.EOF && (jsonReader.NodeType == XmlNodeType.None || jsonReader.NodeType == XmlNodeType.XmlDeclaration)) 227 { 228 // read into <root> node 229 jsonReader.Read(); 230 SkipWhitespace(jsonReader); 231 } 232 233 if (jsonReader.NodeType != XmlNodeType.Element || !String.IsNullOrEmpty(jsonReader.NamespaceURI) || jsonReader.Name != RootElementName) 234 { 235 throw new FormatException(Properties.Resources.IncorrectJsonFormat); 236 } 237 } 238 ReadPrimitive(string type, XmlDictionaryReader jsonReader)239 private static JsonPrimitive ReadPrimitive(string type, XmlDictionaryReader jsonReader) 240 { 241 JsonValue result = null; 242 switch (type) 243 { 244 case NullAttributeValue: 245 jsonReader.Skip(); 246 result = null; 247 break; 248 case BooleanAttributeValue: 249 result = jsonReader.ReadElementContentAsBoolean(); 250 break; 251 case StringAttributeValue: 252 result = jsonReader.ReadElementContentAsString(); 253 break; 254 case NumberAttributeValue: 255 string temp = jsonReader.ReadElementContentAsString(); 256 result = ConvertStringToJsonNumber(temp); 257 break; 258 } 259 260 SkipWhitespace(jsonReader); 261 return (JsonPrimitive)result; 262 } 263 SkipWhitespace(XmlDictionaryReader reader)264 private static void SkipWhitespace(XmlDictionaryReader reader) 265 { 266 while (!reader.EOF && (reader.NodeType == XmlNodeType.Whitespace || reader.NodeType == XmlNodeType.SignificantWhitespace)) 267 { 268 reader.Read(); 269 } 270 } 271 ConvertStringToJsonNumber(string value)272 private static JsonValue ConvertStringToJsonNumber(string value) 273 { 274 if (value.IndexOfAny(_floatingPointChars) < 0) 275 { 276 int intVal; 277 if (Int32.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out intVal)) 278 { 279 return intVal; 280 } 281 282 long longVal; 283 if (Int64.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out longVal)) 284 { 285 return longVal; 286 } 287 } 288 289 decimal decValue; 290 if (Decimal.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out decValue) && decValue != 0) 291 { 292 return decValue; 293 } 294 295 double dblValue; 296 if (Double.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out dblValue)) 297 { 298 return dblValue; 299 } 300 301 throw new ArgumentException(RS.Format(Properties.Resources.InvalidJsonPrimitive, value.ToString()), "value"); 302 } 303 } 304 } 305