1 //------------------------------------------------------------ 2 // Copyright (c) Microsoft Corporation. All rights reserved. 3 //------------------------------------------------------------ 4 5 namespace System.Runtime.Serialization.Json 6 { 7 using System.Diagnostics.CodeAnalysis; 8 using System.Globalization; 9 using System.IO; 10 using System.Runtime; 11 using System.Runtime.Serialization; 12 using System.Security; 13 #if !MONO 14 using System.ServiceModel; 15 #endif 16 using System.Text; 17 using System.Xml; 18 19 class XmlJsonWriter : XmlDictionaryWriter, IXmlJsonWriterInitializer 20 { 21 const char BACK_SLASH = '\\'; 22 const char FORWARD_SLASH = '/'; 23 24 const char HIGH_SURROGATE_START = (char)0xd800; 25 const char LOW_SURROGATE_END = (char)0xdfff; 26 const char MAX_CHAR = (char)0xfffe; 27 const char WHITESPACE = ' '; 28 const char CARRIAGE_RETURN = '\r'; 29 const char NEWLINE = '\n'; 30 const char BACKSPACE = '\b'; 31 const char FORM_FEED = '\f'; 32 const char HORIZONTAL_TABULATION = '\t'; 33 const string xmlNamespace = "http://www.w3.org/XML/1998/namespace"; 34 const string xmlnsNamespace = "http://www.w3.org/2000/xmlns/"; 35 36 [Fx.Tag.SecurityNote(Critical = "Static fields are marked SecurityCritical or readonly to prevent" 37 + " data from being modified or leaked to other components in appdomain.")] 38 [SecurityCritical] 39 static BinHexEncoding binHexEncoding; 40 41 // This array was part of a perf improvement for escaping characters < WHITESPACE. 42 static char[] CharacterAbbrevs; 43 44 string attributeText; 45 JsonDataType dataType; 46 int depth; 47 bool endElementBuffer; 48 bool isWritingDataTypeAttribute; 49 bool isWritingServerTypeAttribute; 50 bool isWritingXmlnsAttribute; 51 bool isWritingXmlnsAttributeDefaultNs; 52 NameState nameState; 53 JsonNodeType nodeType; 54 JsonNodeWriter nodeWriter; 55 JsonNodeType[] scopes; 56 string serverTypeValue; 57 // Do not use this field's value anywhere other than the WriteState property. 58 // It's OK to set this field's value anywhere and then change the WriteState property appropriately. 59 // If it's necessary to check the WriteState outside WriteState, use the WriteState property. 60 WriteState writeState; 61 bool wroteServerTypeAttribute; 62 bool indent; 63 string indentChars; 64 int indentLevel; 65 XmlJsonWriter()66 public XmlJsonWriter() : this(false, null) { } 67 XmlJsonWriter(bool indent, string indentChars)68 public XmlJsonWriter(bool indent, string indentChars) 69 { 70 this.indent = indent; 71 if (indent) 72 { 73 if (indentChars == null) 74 { 75 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("indentChars"); 76 } 77 this.indentChars = indentChars; 78 } 79 InitializeWriter(); 80 81 if (CharacterAbbrevs == null) 82 { 83 CharacterAbbrevs = GetCharacterAbbrevs(); 84 } 85 } 86 GetCharacterAbbrevs()87 private static char[] GetCharacterAbbrevs() 88 { 89 var abbrevs = new char[WHITESPACE]; 90 for(int i = 0; i < WHITESPACE; i++) 91 { 92 char abbrev; 93 if (!LocalAppContextSwitches.DoNotUseEcmaScriptV6EscapeControlCharacter && TryEscapeControlCharacter((char)i, out abbrev)) 94 { 95 abbrevs[i] = abbrev; 96 } 97 else 98 { 99 abbrevs[i] = (char) 0; 100 } 101 } 102 103 return abbrevs; 104 } 105 TryEscapeControlCharacter(char ch, out char abbrev)106 private static bool TryEscapeControlCharacter(char ch, out char abbrev) 107 { 108 switch (ch) 109 { 110 case BACKSPACE: 111 abbrev = 'b'; 112 break; 113 case HORIZONTAL_TABULATION: 114 abbrev = 't'; 115 break; 116 case NEWLINE: 117 abbrev = 'n'; 118 break; 119 case FORM_FEED: 120 abbrev = 'f'; 121 break; 122 case CARRIAGE_RETURN: 123 abbrev = 'r'; 124 break; 125 default: 126 abbrev = ' '; 127 return false; 128 } 129 130 return true; 131 } 132 133 enum JsonDataType 134 { 135 None, 136 Null, 137 Boolean, 138 Number, 139 String, 140 Object, 141 Array 142 }; 143 144 [Flags] 145 enum NameState 146 { 147 None = 0, 148 IsWritingNameWithMapping = 1, 149 IsWritingNameAttribute = 2, 150 WrittenNameWithMapping = 4, 151 } 152 153 public override XmlWriterSettings Settings 154 { 155 // The XmlWriterSettings object used to create this writer instance. 156 // If this writer was not created using the Create method, this property 157 // returns a null reference. 158 get { return null; } 159 } 160 161 public override WriteState WriteState 162 { 163 get 164 { 165 if (writeState == WriteState.Closed) 166 { 167 return WriteState.Closed; 168 } 169 if (HasOpenAttribute) 170 { 171 return WriteState.Attribute; 172 } 173 switch (nodeType) 174 { 175 case JsonNodeType.None: 176 return WriteState.Start; 177 case JsonNodeType.Element: 178 return WriteState.Element; 179 case JsonNodeType.QuotedText: 180 case JsonNodeType.StandaloneText: 181 case JsonNodeType.EndElement: 182 return WriteState.Content; 183 default: 184 return WriteState.Error; 185 } 186 } 187 } 188 189 public override string XmlLang 190 { 191 get { return null; } 192 } 193 194 public override XmlSpace XmlSpace 195 { 196 get { return XmlSpace.None; } 197 } 198 199 static BinHexEncoding BinHexEncoding 200 { 201 [Fx.Tag.SecurityNote(Critical = "Fetches the critical binHexEncoding field.", 202 Safe = "Get-only properties only need to be protected for write; initialized in getter if null.")] 203 [SecuritySafeCritical] 204 get 205 { 206 if (binHexEncoding == null) 207 { 208 binHexEncoding = new BinHexEncoding(); 209 } 210 return binHexEncoding; 211 } 212 } 213 214 bool HasOpenAttribute 215 { 216 get 217 { 218 return (isWritingDataTypeAttribute || isWritingServerTypeAttribute || IsWritingNameAttribute || isWritingXmlnsAttribute); 219 } 220 } 221 222 bool IsClosed 223 { 224 get { return (WriteState == WriteState.Closed); } 225 } 226 227 bool IsWritingCollection 228 { 229 get { return (depth > 0) && (scopes[depth] == JsonNodeType.Collection); } 230 } 231 232 bool IsWritingNameAttribute 233 { 234 get { return (nameState & NameState.IsWritingNameAttribute) == NameState.IsWritingNameAttribute; } 235 } 236 237 bool IsWritingNameWithMapping 238 { 239 get { return (nameState & NameState.IsWritingNameWithMapping) == NameState.IsWritingNameWithMapping; } 240 } 241 242 bool WrittenNameWithMapping 243 { 244 get { return (nameState & NameState.WrittenNameWithMapping) == NameState.WrittenNameWithMapping; } 245 } 246 Close()247 public override void Close() 248 { 249 if (!IsClosed) 250 { 251 try 252 { 253 WriteEndDocument(); 254 } 255 finally 256 { 257 try 258 { 259 nodeWriter.Flush(); 260 nodeWriter.Close(); 261 } 262 finally 263 { 264 writeState = WriteState.Closed; 265 if (depth != 0) 266 { 267 depth = 0; 268 } 269 } 270 } 271 } 272 } 273 Flush()274 public override void Flush() 275 { 276 if (IsClosed) 277 { 278 ThrowClosed(); 279 } 280 nodeWriter.Flush(); 281 } 282 LookupPrefix(string ns)283 public override string LookupPrefix(string ns) 284 { 285 if (ns == null) 286 { 287 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("ns"); 288 } 289 if (ns == Globals.XmlnsNamespace) 290 { 291 return Globals.XmlnsPrefix; 292 } 293 if (ns == xmlNamespace) 294 { 295 return JsonGlobals.xmlPrefix; 296 } 297 if (ns == string.Empty) 298 { 299 return string.Empty; 300 } 301 return null; 302 } 303 SetOutput(Stream stream, Encoding encoding, bool ownsStream)304 public void SetOutput(Stream stream, Encoding encoding, bool ownsStream) 305 { 306 if (stream == null) 307 { 308 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("stream"); 309 } 310 if (encoding == null) 311 { 312 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("encoding"); 313 } 314 if (encoding.WebName != Encoding.UTF8.WebName) 315 { 316 stream = new JsonEncodingStreamWrapper(stream, encoding, false); 317 } 318 else 319 { 320 encoding = null; 321 } 322 if (nodeWriter == null) 323 { 324 nodeWriter = new JsonNodeWriter(); 325 } 326 327 nodeWriter.SetOutput(stream, ownsStream, encoding); 328 InitializeWriter(); 329 } 330 WriteArray(string prefix, string localName, string namespaceUri, bool[] array, int offset, int count)331 public override void WriteArray(string prefix, string localName, string namespaceUri, bool[] array, int offset, int count) 332 { 333 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.JsonWriteArrayNotSupported))); 334 } 335 WriteArray(string prefix, string localName, string namespaceUri, Int16[] array, int offset, int count)336 public override void WriteArray(string prefix, string localName, string namespaceUri, Int16[] array, int offset, int count) 337 { 338 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.JsonWriteArrayNotSupported))); 339 } 340 WriteArray(string prefix, string localName, string namespaceUri, Int32[] array, int offset, int count)341 public override void WriteArray(string prefix, string localName, string namespaceUri, Int32[] array, int offset, int count) 342 { 343 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.JsonWriteArrayNotSupported))); 344 } 345 WriteArray(string prefix, string localName, string namespaceUri, Int64[] array, int offset, int count)346 public override void WriteArray(string prefix, string localName, string namespaceUri, Int64[] array, int offset, int count) 347 { 348 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.JsonWriteArrayNotSupported))); 349 } 350 WriteArray(string prefix, string localName, string namespaceUri, float[] array, int offset, int count)351 public override void WriteArray(string prefix, string localName, string namespaceUri, float[] array, int offset, int count) 352 { 353 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.JsonWriteArrayNotSupported))); 354 } 355 WriteArray(string prefix, string localName, string namespaceUri, double[] array, int offset, int count)356 public override void WriteArray(string prefix, string localName, string namespaceUri, double[] array, int offset, int count) 357 { 358 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.JsonWriteArrayNotSupported))); 359 } 360 WriteArray(string prefix, string localName, string namespaceUri, decimal[] array, int offset, int count)361 public override void WriteArray(string prefix, string localName, string namespaceUri, decimal[] array, int offset, int count) 362 { 363 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.JsonWriteArrayNotSupported))); 364 } 365 WriteArray(string prefix, string localName, string namespaceUri, DateTime[] array, int offset, int count)366 public override void WriteArray(string prefix, string localName, string namespaceUri, DateTime[] array, int offset, int count) 367 { 368 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.JsonWriteArrayNotSupported))); 369 } 370 WriteArray(string prefix, string localName, string namespaceUri, Guid[] array, int offset, int count)371 public override void WriteArray(string prefix, string localName, string namespaceUri, Guid[] array, int offset, int count) 372 { 373 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.JsonWriteArrayNotSupported))); 374 } 375 WriteArray(string prefix, string localName, string namespaceUri, TimeSpan[] array, int offset, int count)376 public override void WriteArray(string prefix, string localName, string namespaceUri, TimeSpan[] array, int offset, int count) 377 { 378 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.JsonWriteArrayNotSupported))); 379 } 380 WriteArray(string prefix, XmlDictionaryString localName, XmlDictionaryString namespaceUri, bool[] array, int offset, int count)381 public override void WriteArray(string prefix, XmlDictionaryString localName, XmlDictionaryString namespaceUri, bool[] array, int offset, int count) 382 { 383 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.JsonWriteArrayNotSupported))); 384 } 385 WriteArray(string prefix, XmlDictionaryString localName, XmlDictionaryString namespaceUri, decimal[] array, int offset, int count)386 public override void WriteArray(string prefix, XmlDictionaryString localName, XmlDictionaryString namespaceUri, decimal[] array, int offset, int count) 387 { 388 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.JsonWriteArrayNotSupported))); 389 } 390 WriteArray(string prefix, XmlDictionaryString localName, XmlDictionaryString namespaceUri, double[] array, int offset, int count)391 public override void WriteArray(string prefix, XmlDictionaryString localName, XmlDictionaryString namespaceUri, double[] array, int offset, int count) 392 { 393 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.JsonWriteArrayNotSupported))); 394 } 395 WriteArray(string prefix, XmlDictionaryString localName, XmlDictionaryString namespaceUri, float[] array, int offset, int count)396 public override void WriteArray(string prefix, XmlDictionaryString localName, XmlDictionaryString namespaceUri, float[] array, int offset, int count) 397 { 398 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.JsonWriteArrayNotSupported))); 399 } 400 WriteArray(string prefix, XmlDictionaryString localName, XmlDictionaryString namespaceUri, int[] array, int offset, int count)401 public override void WriteArray(string prefix, XmlDictionaryString localName, XmlDictionaryString namespaceUri, int[] array, int offset, int count) 402 { 403 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.JsonWriteArrayNotSupported))); 404 } 405 WriteArray(string prefix, XmlDictionaryString localName, XmlDictionaryString namespaceUri, long[] array, int offset, int count)406 public override void WriteArray(string prefix, XmlDictionaryString localName, XmlDictionaryString namespaceUri, long[] array, int offset, int count) 407 { 408 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.JsonWriteArrayNotSupported))); 409 } 410 WriteArray(string prefix, XmlDictionaryString localName, XmlDictionaryString namespaceUri, short[] array, int offset, int count)411 public override void WriteArray(string prefix, XmlDictionaryString localName, XmlDictionaryString namespaceUri, short[] array, int offset, int count) 412 { 413 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.JsonWriteArrayNotSupported))); 414 } 415 WriteArray(string prefix, XmlDictionaryString localName, XmlDictionaryString namespaceUri, DateTime[] array, int offset, int count)416 public override void WriteArray(string prefix, XmlDictionaryString localName, XmlDictionaryString namespaceUri, DateTime[] array, int offset, int count) 417 { 418 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.JsonWriteArrayNotSupported))); 419 } 420 WriteArray(string prefix, XmlDictionaryString localName, XmlDictionaryString namespaceUri, Guid[] array, int offset, int count)421 public override void WriteArray(string prefix, XmlDictionaryString localName, XmlDictionaryString namespaceUri, Guid[] array, int offset, int count) 422 { 423 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.JsonWriteArrayNotSupported))); 424 } 425 WriteArray(string prefix, XmlDictionaryString localName, XmlDictionaryString namespaceUri, TimeSpan[] array, int offset, int count)426 public override void WriteArray(string prefix, XmlDictionaryString localName, XmlDictionaryString namespaceUri, TimeSpan[] array, int offset, int count) 427 { 428 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.JsonWriteArrayNotSupported))); 429 } 430 WriteBase64(byte[] buffer, int index, int count)431 public override void WriteBase64(byte[] buffer, int index, int count) 432 { 433 if (buffer == null) 434 { 435 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("buffer"); 436 } 437 438 // Not checking upper bound because it will be caught by "count". This is what XmlTextWriter does. 439 if (index < 0) 440 { 441 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( 442 new ArgumentOutOfRangeException("index", SR.GetString(SR.ValueMustBeNonNegative))); 443 } 444 445 if (count < 0) 446 { 447 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( 448 new ArgumentOutOfRangeException("count", SR.GetString(SR.ValueMustBeNonNegative))); 449 } 450 if (count > buffer.Length - index) 451 { 452 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( 453 new ArgumentOutOfRangeException("count", 454 SR.GetString(SR.JsonSizeExceedsRemainingBufferSpace, 455 buffer.Length - index))); 456 } 457 458 StartText(); 459 nodeWriter.WriteBase64Text(buffer, 0, buffer, index, count); 460 } 461 WriteBinHex(byte[] buffer, int index, int count)462 public override void WriteBinHex(byte[] buffer, int index, int count) 463 { 464 if (buffer == null) 465 { 466 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("buffer"); 467 } 468 469 // Not checking upper bound because it will be caught by "count". This is what XmlTextWriter does. 470 if (index < 0) 471 { 472 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( 473 new ArgumentOutOfRangeException("index", SR.GetString(SR.ValueMustBeNonNegative))); 474 } 475 476 if (count < 0) 477 { 478 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( 479 new ArgumentOutOfRangeException("count", SR.GetString(SR.ValueMustBeNonNegative))); 480 } 481 if (count > buffer.Length - index) 482 { 483 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( 484 new ArgumentOutOfRangeException("count", 485 SR.GetString(SR.JsonSizeExceedsRemainingBufferSpace, 486 buffer.Length - index))); 487 } 488 489 StartText(); 490 WriteEscapedJsonString(BinHexEncoding.GetString(buffer, index, count)); 491 } 492 WriteCData(string text)493 public override void WriteCData(string text) 494 { 495 WriteString(text); 496 } 497 WriteCharEntity(char ch)498 public override void WriteCharEntity(char ch) 499 { 500 WriteString(ch.ToString()); 501 } 502 WriteChars(char[] buffer, int index, int count)503 public override void WriteChars(char[] buffer, int index, int count) 504 { 505 if (buffer == null) 506 { 507 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("buffer"); 508 } 509 510 // Not checking upper bound because it will be caught by "count". This is what XmlTextWriter does. 511 if (index < 0) 512 { 513 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( 514 new ArgumentOutOfRangeException("index", SR.GetString(SR.ValueMustBeNonNegative))); 515 } 516 517 if (count < 0) 518 { 519 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( 520 new ArgumentOutOfRangeException("count", SR.GetString(SR.ValueMustBeNonNegative))); 521 } 522 if (count > buffer.Length - index) 523 { 524 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( 525 new ArgumentOutOfRangeException("count", 526 SR.GetString(SR.JsonSizeExceedsRemainingBufferSpace, 527 buffer.Length - index))); 528 } 529 530 WriteString(new string(buffer, index, count)); 531 } 532 WriteComment(string text)533 public override void WriteComment(string text) 534 { 535 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.JsonMethodNotSupported, "WriteComment"))); 536 } 537 538 [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "2#sysid", Justification = "This method is derived from the base")] 539 [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "1#pubid", Justification = "This method is derived from the base")] WriteDocType(string name, string pubid, string sysid, string subset)540 public override void WriteDocType(string name, string pubid, string sysid, string subset) 541 { 542 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.JsonMethodNotSupported, "WriteDocType"))); 543 } 544 WriteEndAttribute()545 public override void WriteEndAttribute() 546 { 547 if (IsClosed) 548 { 549 ThrowClosed(); 550 } 551 if (!HasOpenAttribute) 552 { 553 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( 554 new XmlException(SR.GetString(SR.JsonNoMatchingStartAttribute))); 555 } 556 557 Fx.Assert(!(isWritingDataTypeAttribute && isWritingServerTypeAttribute), 558 "Can not write type attribute and __type attribute at the same time."); 559 560 if (isWritingDataTypeAttribute) 561 { 562 switch (attributeText) 563 { 564 case JsonGlobals.numberString: 565 { 566 ThrowIfServerTypeWritten(JsonGlobals.numberString); 567 dataType = JsonDataType.Number; 568 break; 569 } 570 case JsonGlobals.stringString: 571 { 572 ThrowIfServerTypeWritten(JsonGlobals.stringString); 573 dataType = JsonDataType.String; 574 break; 575 } 576 case JsonGlobals.arrayString: 577 { 578 ThrowIfServerTypeWritten(JsonGlobals.arrayString); 579 dataType = JsonDataType.Array; 580 break; 581 } 582 case JsonGlobals.objectString: 583 { 584 dataType = JsonDataType.Object; 585 break; 586 } 587 case JsonGlobals.nullString: 588 { 589 ThrowIfServerTypeWritten(JsonGlobals.nullString); 590 dataType = JsonDataType.Null; 591 break; 592 } 593 case JsonGlobals.booleanString: 594 { 595 ThrowIfServerTypeWritten(JsonGlobals.booleanString); 596 dataType = JsonDataType.Boolean; 597 break; 598 } 599 default: 600 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( 601 new XmlException(SR.GetString(SR.JsonUnexpectedAttributeValue, attributeText))); 602 } 603 604 attributeText = null; 605 isWritingDataTypeAttribute = false; 606 607 if (!IsWritingNameWithMapping || WrittenNameWithMapping) 608 { 609 WriteDataTypeServerType(); 610 } 611 } 612 else if (isWritingServerTypeAttribute) 613 { 614 serverTypeValue = attributeText; 615 attributeText = null; 616 isWritingServerTypeAttribute = false; 617 618 // we are writing __type after type="object" (enforced by WSE) 619 if ((!IsWritingNameWithMapping || WrittenNameWithMapping) && dataType == JsonDataType.Object) 620 { 621 WriteServerTypeAttribute(); 622 } 623 } 624 else if (IsWritingNameAttribute) 625 { 626 WriteJsonElementName(attributeText); 627 attributeText = null; 628 nameState = NameState.IsWritingNameWithMapping | NameState.WrittenNameWithMapping; 629 WriteDataTypeServerType(); 630 } 631 else if (isWritingXmlnsAttribute) 632 { 633 if (!string.IsNullOrEmpty(attributeText) && isWritingXmlnsAttributeDefaultNs) 634 { 635 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("ns", SR.GetString(SR.JsonNamespaceMustBeEmpty, attributeText)); 636 } 637 638 attributeText = null; 639 isWritingXmlnsAttribute = false; 640 isWritingXmlnsAttributeDefaultNs = false; 641 } 642 } 643 WriteEndDocument()644 public override void WriteEndDocument() 645 { 646 if (IsClosed) 647 { 648 ThrowClosed(); 649 } 650 if (nodeType != JsonNodeType.None) 651 { 652 while (depth > 0) 653 { 654 WriteEndElement(); 655 } 656 } 657 } 658 WriteEndElement()659 public override void WriteEndElement() 660 { 661 if (IsClosed) 662 { 663 ThrowClosed(); 664 } 665 666 if (depth == 0) 667 { 668 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( 669 new XmlException(SR.GetString(SR.JsonEndElementNoOpenNodes))); 670 } 671 if (HasOpenAttribute) 672 { 673 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( 674 new XmlException(SR.GetString(SR.JsonOpenAttributeMustBeClosedFirst, "WriteEndElement"))); 675 } 676 677 endElementBuffer = false; 678 679 JsonNodeType token = ExitScope(); 680 if (token == JsonNodeType.Collection) 681 { 682 indentLevel--; 683 if (indent) 684 { 685 if (nodeType == JsonNodeType.Element) 686 { 687 nodeWriter.WriteText(WHITESPACE); 688 } 689 else 690 { 691 WriteNewLine(); 692 WriteIndent(); 693 } 694 } 695 nodeWriter.WriteText(JsonGlobals.EndCollectionChar); 696 token = ExitScope(); 697 } 698 else if (nodeType == JsonNodeType.QuotedText) 699 { 700 // For writing " 701 WriteJsonQuote(); 702 } 703 else if (nodeType == JsonNodeType.Element) 704 { 705 if ((dataType == JsonDataType.None) && (serverTypeValue != null)) 706 { 707 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( 708 new XmlException(SR.GetString(SR.JsonMustSpecifyDataType, 709 JsonGlobals.typeString, JsonGlobals.objectString, JsonGlobals.serverTypeString))); 710 } 711 712 if (IsWritingNameWithMapping && !WrittenNameWithMapping) 713 { 714 // Ending </item> without writing item attribute 715 // Not providing a better error message because localization deadline has passed. 716 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( 717 new XmlException(SR.GetString(SR.JsonMustSpecifyDataType, 718 JsonGlobals.itemString, string.Empty, JsonGlobals.itemString))); 719 } 720 721 // the element is empty, it does not have any content, 722 if ((dataType == JsonDataType.None) || 723 (dataType == JsonDataType.String)) 724 { 725 nodeWriter.WriteText(JsonGlobals.QuoteChar); 726 nodeWriter.WriteText(JsonGlobals.QuoteChar); 727 } 728 } 729 else 730 { 731 // Assert on only StandaloneText and EndElement because preceding if 732 // conditions take care of checking for QuotedText and Element. 733 Fx.Assert((nodeType == JsonNodeType.StandaloneText) || (nodeType == JsonNodeType.EndElement), 734 "nodeType has invalid value " + nodeType + ". Expected it to be QuotedText, Element, StandaloneText, or EndElement."); 735 } 736 if (depth != 0) 737 { 738 if (token == JsonNodeType.Element) 739 { 740 endElementBuffer = true; 741 } 742 else if (token == JsonNodeType.Object) 743 { 744 indentLevel--; 745 if (indent) 746 { 747 if (nodeType == JsonNodeType.Element) 748 { 749 nodeWriter.WriteText(WHITESPACE); 750 } 751 else 752 { 753 WriteNewLine(); 754 WriteIndent(); 755 } 756 } 757 nodeWriter.WriteText(JsonGlobals.EndObjectChar); 758 if ((depth > 0) && scopes[depth] == JsonNodeType.Element) 759 { 760 ExitScope(); 761 endElementBuffer = true; 762 } 763 } 764 } 765 766 dataType = JsonDataType.None; 767 nodeType = JsonNodeType.EndElement; 768 nameState = NameState.None; 769 wroteServerTypeAttribute = false; 770 } 771 WriteEntityRef(string name)772 public override void WriteEntityRef(string name) 773 { 774 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.JsonMethodNotSupported, "WriteEntityRef"))); 775 } 776 WriteFullEndElement()777 public override void WriteFullEndElement() 778 { 779 WriteEndElement(); 780 } 781 WriteProcessingInstruction(string name, string text)782 public override void WriteProcessingInstruction(string name, string text) 783 { 784 if (IsClosed) 785 { 786 ThrowClosed(); 787 } 788 789 if (!name.Equals("xml", StringComparison.OrdinalIgnoreCase)) 790 { 791 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.JsonXmlProcessingInstructionNotSupported), "name")); 792 } 793 794 if (WriteState != WriteState.Start) 795 { 796 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.JsonXmlInvalidDeclaration))); 797 } 798 } 799 WriteQualifiedName(string localName, string ns)800 public override void WriteQualifiedName(string localName, string ns) 801 { 802 if (localName == null) 803 { 804 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("localName"); 805 } 806 if (localName.Length == 0) 807 { 808 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("localName", 809 SR.GetString(SR.JsonInvalidLocalNameEmpty)); 810 } 811 if (ns == null) 812 { 813 ns = string.Empty; 814 } 815 816 base.WriteQualifiedName(localName, ns); 817 } 818 WriteRaw(string data)819 public override void WriteRaw(string data) 820 { 821 WriteString(data); 822 } 823 WriteRaw(char[] buffer, int index, int count)824 public override void WriteRaw(char[] buffer, int index, int count) 825 { 826 if (buffer == null) 827 { 828 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("buffer"); 829 } 830 831 // Not checking upper bound because it will be caught by "count". This is what XmlTextWriter does. 832 if (index < 0) 833 { 834 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( 835 new ArgumentOutOfRangeException("index", SR.GetString(SR.ValueMustBeNonNegative))); 836 } 837 838 if (count < 0) 839 { 840 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( 841 new ArgumentOutOfRangeException("count", SR.GetString(SR.ValueMustBeNonNegative))); 842 } 843 if (count > buffer.Length - index) 844 { 845 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( 846 new ArgumentOutOfRangeException("count", 847 SR.GetString(SR.JsonSizeExceedsRemainingBufferSpace, 848 buffer.Length - index))); 849 } 850 851 WriteString(new string(buffer, index, count)); 852 } 853 854 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase")] // Microsoft, ToLowerInvariant is just used in Json error message WriteStartAttribute(string prefix, string localName, string ns)855 public override void WriteStartAttribute(string prefix, string localName, string ns) 856 { 857 if (IsClosed) 858 { 859 ThrowClosed(); 860 } 861 if (!string.IsNullOrEmpty(prefix)) 862 { 863 if (IsWritingNameWithMapping && prefix == JsonGlobals.xmlnsPrefix) 864 { 865 if (ns != null && ns != xmlnsNamespace) 866 { 867 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(System.Runtime.Serialization.SR.GetString(System.Runtime.Serialization.SR.XmlPrefixBoundToNamespace, "xmlns", xmlnsNamespace, ns), "ns")); 868 } 869 } 870 else 871 { 872 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("prefix", SR.GetString(SR.JsonPrefixMustBeNullOrEmpty, prefix)); 873 } 874 } 875 else 876 { 877 if (IsWritingNameWithMapping && ns == xmlnsNamespace && localName != JsonGlobals.xmlnsPrefix) 878 { 879 prefix = JsonGlobals.xmlnsPrefix; 880 } 881 } 882 if (!string.IsNullOrEmpty(ns)) 883 { 884 if (IsWritingNameWithMapping && ns == xmlnsNamespace) 885 { 886 prefix = JsonGlobals.xmlnsPrefix; 887 } 888 else if (string.IsNullOrEmpty(prefix) && localName == JsonGlobals.xmlnsPrefix && ns == xmlnsNamespace) 889 { 890 prefix = JsonGlobals.xmlnsPrefix; 891 isWritingXmlnsAttributeDefaultNs = true; 892 } 893 else 894 { 895 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("ns", SR.GetString(SR.JsonNamespaceMustBeEmpty, ns)); 896 } 897 } 898 if (localName == null) 899 { 900 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("localName"); 901 } 902 if (localName.Length == 0) 903 { 904 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("localName", SR.GetString(SR.JsonInvalidLocalNameEmpty)); 905 } 906 if ((nodeType != JsonNodeType.Element) && !wroteServerTypeAttribute) 907 { 908 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.JsonAttributeMustHaveElement))); 909 } 910 if (HasOpenAttribute) 911 { 912 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( 913 new XmlException(SR.GetString(SR.JsonOpenAttributeMustBeClosedFirst, "WriteStartAttribute"))); 914 } 915 if (prefix == JsonGlobals.xmlnsPrefix) 916 { 917 isWritingXmlnsAttribute = true; 918 } 919 else if (localName == JsonGlobals.typeString) 920 { 921 if (dataType != JsonDataType.None) 922 { 923 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( 924 new XmlException(SR.GetString(SR.JsonAttributeAlreadyWritten, JsonGlobals.typeString))); 925 } 926 927 isWritingDataTypeAttribute = true; 928 } 929 else if (localName == JsonGlobals.serverTypeString) 930 { 931 if (serverTypeValue != null) 932 { 933 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( 934 new XmlException(SR.GetString(SR.JsonAttributeAlreadyWritten, JsonGlobals.serverTypeString))); 935 } 936 937 if ((dataType != JsonDataType.None) && (dataType != JsonDataType.Object)) 938 { 939 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( 940 new XmlException(SR.GetString(SR.JsonServerTypeSpecifiedForInvalidDataType, 941 JsonGlobals.serverTypeString, JsonGlobals.typeString, dataType.ToString().ToLowerInvariant(), JsonGlobals.objectString))); 942 } 943 944 isWritingServerTypeAttribute = true; 945 } 946 else if (localName == JsonGlobals.itemString) 947 { 948 if (WrittenNameWithMapping) 949 { 950 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( 951 new XmlException(SR.GetString(SR.JsonAttributeAlreadyWritten, JsonGlobals.itemString))); 952 } 953 954 if (!IsWritingNameWithMapping) 955 { 956 // Don't write attribute with local name "item" if <item> element is not open. 957 // Not providing a better error message because localization deadline has passed. 958 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( 959 new XmlException(SR.GetString(SR.JsonEndElementNoOpenNodes))); 960 } 961 962 nameState |= NameState.IsWritingNameAttribute; 963 } 964 else 965 { 966 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("localName", SR.GetString(SR.JsonUnexpectedAttributeLocalName, localName)); 967 } 968 } 969 WriteStartDocument(bool standalone)970 public override void WriteStartDocument(bool standalone) 971 { 972 // In XML, writes the XML declaration with the version "1.0" and the standalone attribute. 973 WriteStartDocument(); 974 } 975 WriteStartDocument()976 public override void WriteStartDocument() 977 { 978 // In XML, writes the XML declaration with the version "1.0". 979 if (IsClosed) 980 { 981 ThrowClosed(); 982 } 983 if (WriteState != WriteState.Start) 984 { 985 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( 986 new XmlException( 987 SR.GetString(SR.JsonInvalidWriteState, "WriteStartDocument", WriteState.ToString()))); 988 } 989 } 990 WriteStartElement(string prefix, string localName, string ns)991 public override void WriteStartElement(string prefix, string localName, string ns) 992 { 993 if (localName == null) 994 { 995 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("localName"); 996 } 997 if (localName.Length == 0) 998 { 999 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("localName", 1000 SR.GetString(SR.JsonInvalidLocalNameEmpty)); 1001 } 1002 if (!string.IsNullOrEmpty(prefix)) 1003 { 1004 if (string.IsNullOrEmpty(ns) || !TrySetWritingNameWithMapping(localName, ns)) 1005 { 1006 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("prefix", SR.GetString(SR.JsonPrefixMustBeNullOrEmpty, prefix)); 1007 } 1008 } 1009 if (!string.IsNullOrEmpty(ns)) 1010 { 1011 if (!TrySetWritingNameWithMapping(localName, ns)) 1012 { 1013 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("ns", SR.GetString(SR.JsonNamespaceMustBeEmpty, ns)); 1014 } 1015 } 1016 if (IsClosed) 1017 { 1018 ThrowClosed(); 1019 } 1020 if (HasOpenAttribute) 1021 { 1022 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( 1023 new XmlException(SR.GetString(SR.JsonOpenAttributeMustBeClosedFirst, "WriteStartElement"))); 1024 } 1025 if ((nodeType != JsonNodeType.None) && depth == 0) 1026 { 1027 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( 1028 new XmlException(SR.GetString(SR.JsonMultipleRootElementsNotAllowedOnWriter))); 1029 } 1030 1031 switch (nodeType) 1032 { 1033 case JsonNodeType.None: 1034 { 1035 if (!localName.Equals(JsonGlobals.rootString)) 1036 { 1037 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( 1038 new XmlException(SR.GetString(SR.JsonInvalidRootElementName, localName, JsonGlobals.rootString))); 1039 } 1040 EnterScope(JsonNodeType.Element); 1041 break; 1042 } 1043 case JsonNodeType.Element: 1044 { 1045 if ((dataType != JsonDataType.Array) && (dataType != JsonDataType.Object)) 1046 { 1047 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( 1048 new XmlException(SR.GetString(SR.JsonNodeTypeArrayOrObjectNotSpecified))); 1049 } 1050 if (indent) 1051 { 1052 WriteNewLine(); 1053 WriteIndent(); 1054 } 1055 if (!IsWritingCollection) 1056 { 1057 if (nameState != NameState.IsWritingNameWithMapping) 1058 { 1059 WriteJsonElementName(localName); 1060 } 1061 } 1062 else if (!localName.Equals(JsonGlobals.itemString)) 1063 { 1064 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( 1065 new XmlException(SR.GetString(SR.JsonInvalidItemNameForArrayElement, localName, JsonGlobals.itemString))); 1066 } 1067 EnterScope(JsonNodeType.Element); 1068 break; 1069 } 1070 case JsonNodeType.EndElement: 1071 { 1072 if (endElementBuffer) 1073 { 1074 nodeWriter.WriteText(JsonGlobals.MemberSeparatorChar); 1075 } 1076 if (indent) 1077 { 1078 WriteNewLine(); 1079 WriteIndent(); 1080 } 1081 if (!IsWritingCollection) 1082 { 1083 if (nameState != NameState.IsWritingNameWithMapping) 1084 { 1085 WriteJsonElementName(localName); 1086 } 1087 } 1088 else if (!localName.Equals(JsonGlobals.itemString)) 1089 { 1090 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( 1091 new XmlException(SR.GetString(SR.JsonInvalidItemNameForArrayElement, localName, JsonGlobals.itemString))); 1092 } 1093 EnterScope(JsonNodeType.Element); 1094 break; 1095 } 1096 default: 1097 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( 1098 new XmlException(SR.GetString(SR.JsonInvalidStartElementCall))); 1099 } 1100 1101 isWritingDataTypeAttribute = false; 1102 isWritingServerTypeAttribute = false; 1103 isWritingXmlnsAttribute = false; 1104 wroteServerTypeAttribute = false; 1105 serverTypeValue = null; 1106 dataType = JsonDataType.None; 1107 nodeType = JsonNodeType.Element; 1108 } 1109 WriteString(string text)1110 public override void WriteString(string text) 1111 { 1112 if (HasOpenAttribute && (text != null)) 1113 { 1114 attributeText += text; 1115 } 1116 else 1117 { 1118 if (text == null) 1119 { 1120 text = string.Empty; 1121 } 1122 1123 // do work only when not indenting whitespaces 1124 if (!((this.dataType == JsonDataType.Array || this.dataType == JsonDataType.Object || this.nodeType == JsonNodeType.EndElement) && XmlConverter.IsWhitespace(text))) 1125 { 1126 StartText(); 1127 WriteEscapedJsonString(text); 1128 } 1129 } 1130 } 1131 WriteSurrogateCharEntity(char lowChar, char highChar)1132 public override void WriteSurrogateCharEntity(char lowChar, char highChar) 1133 { 1134 WriteString(string.Concat(highChar, lowChar)); 1135 } 1136 WriteValue(bool value)1137 public override void WriteValue(bool value) 1138 { 1139 StartText(); 1140 nodeWriter.WriteBoolText(value); 1141 } 1142 WriteValue(decimal value)1143 public override void WriteValue(decimal value) 1144 { 1145 StartText(); 1146 nodeWriter.WriteDecimalText(value); 1147 } 1148 WriteValue(double value)1149 public override void WriteValue(double value) 1150 { 1151 StartText(); 1152 nodeWriter.WriteDoubleText(value); 1153 } 1154 WriteValue(float value)1155 public override void WriteValue(float value) 1156 { 1157 StartText(); 1158 nodeWriter.WriteFloatText(value); 1159 } 1160 WriteValue(int value)1161 public override void WriteValue(int value) 1162 { 1163 StartText(); 1164 nodeWriter.WriteInt32Text(value); 1165 } 1166 WriteValue(long value)1167 public override void WriteValue(long value) 1168 { 1169 StartText(); 1170 nodeWriter.WriteInt64Text(value); 1171 } 1172 WriteValue(Guid value)1173 public override void WriteValue(Guid value) 1174 { 1175 StartText(); 1176 nodeWriter.WriteGuidText(value); 1177 } 1178 WriteValue(DateTime value)1179 public override void WriteValue(DateTime value) 1180 { 1181 StartText(); 1182 nodeWriter.WriteDateTimeText(value); 1183 } 1184 WriteValue(string value)1185 public override void WriteValue(string value) 1186 { 1187 WriteString(value); 1188 } 1189 WriteValue(TimeSpan value)1190 public override void WriteValue(TimeSpan value) 1191 { 1192 StartText(); 1193 nodeWriter.WriteTimeSpanText(value); 1194 } 1195 WriteValue(UniqueId value)1196 public override void WriteValue(UniqueId value) 1197 { 1198 if (value == null) 1199 { 1200 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value"); 1201 } 1202 1203 StartText(); 1204 nodeWriter.WriteUniqueIdText(value); 1205 } 1206 WriteValue(object value)1207 public override void WriteValue(object value) 1208 { 1209 if (IsClosed) 1210 { 1211 ThrowClosed(); 1212 } 1213 1214 if (value == null) 1215 { 1216 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value"); 1217 } 1218 1219 if (value is Array) 1220 { 1221 WriteValue((Array)value); 1222 } 1223 else if (value is IStreamProvider) 1224 { 1225 WriteValue((IStreamProvider)value); 1226 } 1227 else 1228 { 1229 WritePrimitiveValue(value); 1230 } 1231 } 1232 1233 [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "Whitespace", Justification = "This method is derived from the base")] WriteWhitespace(string ws)1234 public override void WriteWhitespace(string ws) 1235 { 1236 if (IsClosed) 1237 { 1238 ThrowClosed(); 1239 } 1240 if (ws == null) 1241 { 1242 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("ws"); 1243 } 1244 1245 for (int i = 0; i < ws.Length; ++i) 1246 { 1247 char c = ws[i]; 1248 if (c != ' ' && 1249 c != '\t' && 1250 c != '\n' && 1251 c != '\r') 1252 { 1253 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("ws", 1254 SR.GetString(SR.JsonOnlyWhitespace, c.ToString(), "WriteWhitespace")); 1255 } 1256 } 1257 1258 WriteString(ws); 1259 } 1260 WriteXmlAttribute(string localName, string value)1261 public override void WriteXmlAttribute(string localName, string value) 1262 { 1263 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.JsonMethodNotSupported, "WriteXmlAttribute"))); 1264 } 1265 WriteXmlAttribute(XmlDictionaryString localName, XmlDictionaryString value)1266 public override void WriteXmlAttribute(XmlDictionaryString localName, XmlDictionaryString value) 1267 { 1268 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.JsonMethodNotSupported, "WriteXmlAttribute"))); 1269 } 1270 WriteXmlnsAttribute(string prefix, string namespaceUri)1271 public override void WriteXmlnsAttribute(string prefix, string namespaceUri) 1272 { 1273 if (!IsWritingNameWithMapping) 1274 { 1275 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.JsonMethodNotSupported, "WriteXmlnsAttribute"))); 1276 } 1277 } 1278 WriteXmlnsAttribute(string prefix, XmlDictionaryString namespaceUri)1279 public override void WriteXmlnsAttribute(string prefix, XmlDictionaryString namespaceUri) 1280 { 1281 if (!IsWritingNameWithMapping) 1282 { 1283 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.JsonMethodNotSupported, "WriteXmlnsAttribute"))); 1284 } 1285 } 1286 CharacterNeedsEscaping(char ch)1287 internal static bool CharacterNeedsEscaping(char ch) 1288 { 1289 return (ch == FORWARD_SLASH || ch == JsonGlobals.QuoteChar || ch < WHITESPACE || ch == BACK_SLASH 1290 || (ch >= HIGH_SURROGATE_START && (ch <= LOW_SURROGATE_END || ch >= MAX_CHAR))); 1291 } 1292 1293 ThrowClosed()1294 static void ThrowClosed() 1295 { 1296 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( 1297 new InvalidOperationException(SR.GetString(SR.JsonWriterClosed))); 1298 } 1299 CheckText(JsonNodeType nextNodeType)1300 void CheckText(JsonNodeType nextNodeType) 1301 { 1302 if (IsClosed) 1303 { 1304 ThrowClosed(); 1305 } 1306 if (depth == 0) 1307 { 1308 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError( 1309 new InvalidOperationException( 1310 System.Runtime.Serialization.SR.GetString(System.Runtime.Serialization.SR.XmlIllegalOutsideRoot))); 1311 } 1312 1313 if ((nextNodeType == JsonNodeType.StandaloneText) && 1314 (nodeType == JsonNodeType.QuotedText)) 1315 { 1316 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( 1317 new XmlException( 1318 SR.GetString(SR.JsonCannotWriteStandaloneTextAfterQuotedText))); 1319 } 1320 } 1321 EnterScope(JsonNodeType currentNodeType)1322 void EnterScope(JsonNodeType currentNodeType) 1323 { 1324 depth++; 1325 if (scopes == null) 1326 { 1327 scopes = new JsonNodeType[4]; 1328 } 1329 else if (scopes.Length == depth) 1330 { 1331 JsonNodeType[] newScopes = new JsonNodeType[depth * 2]; 1332 Array.Copy(scopes, newScopes, depth); 1333 scopes = newScopes; 1334 } 1335 scopes[depth] = currentNodeType; 1336 } 1337 ExitScope()1338 JsonNodeType ExitScope() 1339 { 1340 JsonNodeType nodeTypeToReturn = scopes[depth]; 1341 scopes[depth] = JsonNodeType.None; 1342 depth--; 1343 return nodeTypeToReturn; 1344 } 1345 InitializeWriter()1346 void InitializeWriter() 1347 { 1348 nodeType = JsonNodeType.None; 1349 dataType = JsonDataType.None; 1350 isWritingDataTypeAttribute = false; 1351 wroteServerTypeAttribute = false; 1352 isWritingServerTypeAttribute = false; 1353 serverTypeValue = null; 1354 attributeText = null; 1355 1356 if (depth != 0) 1357 { 1358 depth = 0; 1359 } 1360 if ((scopes != null) && (scopes.Length > JsonGlobals.maxScopeSize)) 1361 { 1362 scopes = null; 1363 } 1364 1365 // Can't let writeState be at Closed if reinitializing. 1366 writeState = WriteState.Start; 1367 endElementBuffer = false; 1368 indentLevel = 0; 1369 } 1370 IsUnicodeNewlineCharacter(char c)1371 static bool IsUnicodeNewlineCharacter(char c) 1372 { 1373 // Newline characters in JSON strings need to be encoded on the way out (DevDiv #665974) 1374 // See Unicode 6.2, Table 5-1 (http://www.unicode.org/versions/Unicode6.2.0/ch05.pdf]) for the full list. 1375 1376 // We only care about NEL, LS, and PS, since the other newline characters are all 1377 // control characters so are already encoded. 1378 return (c == '\u0085' || c == '\u2028' || c == '\u2029'); 1379 } 1380 StartText()1381 void StartText() 1382 { 1383 if (HasOpenAttribute) 1384 { 1385 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.JsonMustUseWriteStringForWritingAttributeValues))); 1386 } 1387 1388 if ((dataType == JsonDataType.None) && (serverTypeValue != null)) 1389 { 1390 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( 1391 new XmlException(SR.GetString(SR.JsonMustSpecifyDataType, 1392 JsonGlobals.typeString, JsonGlobals.objectString, JsonGlobals.serverTypeString))); 1393 } 1394 1395 if (IsWritingNameWithMapping && !WrittenNameWithMapping) 1396 { 1397 // Don't write out any text content unless the local name has been written. 1398 // Not providing a better error message because localization deadline has passed. 1399 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( 1400 new XmlException(SR.GetString(SR.JsonMustSpecifyDataType, 1401 JsonGlobals.itemString, string.Empty, JsonGlobals.itemString))); 1402 } 1403 1404 if ((dataType == JsonDataType.String) || 1405 (dataType == JsonDataType.None)) 1406 { 1407 CheckText(JsonNodeType.QuotedText); 1408 if (nodeType != JsonNodeType.QuotedText) 1409 { 1410 WriteJsonQuote(); 1411 } 1412 nodeType = JsonNodeType.QuotedText; 1413 } 1414 else if ((dataType == JsonDataType.Number) || 1415 (dataType == JsonDataType.Boolean)) 1416 { 1417 CheckText(JsonNodeType.StandaloneText); 1418 nodeType = JsonNodeType.StandaloneText; 1419 } 1420 else 1421 { 1422 ThrowInvalidAttributeContent(); 1423 } 1424 } 1425 ThrowIfServerTypeWritten(string dataTypeSpecified)1426 void ThrowIfServerTypeWritten(string dataTypeSpecified) 1427 { 1428 if (serverTypeValue != null) 1429 { 1430 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( 1431 new XmlException(SR.GetString(SR.JsonInvalidDataTypeSpecifiedForServerType, 1432 JsonGlobals.typeString, dataTypeSpecified, JsonGlobals.serverTypeString, JsonGlobals.objectString))); 1433 } 1434 } 1435 1436 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase")] // Microsoft, ToLowerInvariant is just used in Json error message ThrowInvalidAttributeContent()1437 void ThrowInvalidAttributeContent() 1438 { 1439 if (HasOpenAttribute) 1440 { 1441 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( 1442 new XmlException(SR.GetString(SR.JsonInvalidMethodBetweenStartEndAttribute))); 1443 } 1444 else 1445 { 1446 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( 1447 new XmlException(SR.GetString(SR.JsonCannotWriteTextAfterNonTextAttribute, 1448 dataType.ToString().ToLowerInvariant()))); 1449 } 1450 } 1451 TrySetWritingNameWithMapping(string localName, string ns)1452 bool TrySetWritingNameWithMapping(string localName, string ns) 1453 { 1454 if (localName.Equals(JsonGlobals.itemString) && ns.Equals(JsonGlobals.itemString)) 1455 { 1456 nameState = NameState.IsWritingNameWithMapping; 1457 return true; 1458 } 1459 return false; 1460 } 1461 WriteDataTypeServerType()1462 void WriteDataTypeServerType() 1463 { 1464 if (dataType != JsonDataType.None) 1465 { 1466 switch (dataType) 1467 { 1468 case JsonDataType.Array: 1469 { 1470 EnterScope(JsonNodeType.Collection); 1471 nodeWriter.WriteText(JsonGlobals.CollectionChar); 1472 indentLevel++; 1473 break; 1474 } 1475 case JsonDataType.Object: 1476 { 1477 EnterScope(JsonNodeType.Object); 1478 nodeWriter.WriteText(JsonGlobals.ObjectChar); 1479 indentLevel++; 1480 break; 1481 } 1482 case JsonDataType.Null: 1483 { 1484 nodeWriter.WriteText(JsonGlobals.nullString); 1485 break; 1486 } 1487 default: 1488 break; 1489 } 1490 1491 if (serverTypeValue != null) 1492 { 1493 // dataType must be object because we throw in all other case. 1494 WriteServerTypeAttribute(); 1495 } 1496 } 1497 } 1498 1499 [SecuritySafeCritical] WriteEscapedJsonString(string str)1500 unsafe void WriteEscapedJsonString(string str) 1501 { 1502 fixed (char* chars = str) 1503 { 1504 int i = 0; 1505 int j; 1506 for (j = 0; j < str.Length; j++) 1507 { 1508 char ch = chars[j]; 1509 if (ch <= FORWARD_SLASH) 1510 { 1511 if (ch == FORWARD_SLASH || ch == JsonGlobals.QuoteChar) 1512 { 1513 nodeWriter.WriteChars(chars + i, j - i); 1514 nodeWriter.WriteText(BACK_SLASH); 1515 nodeWriter.WriteText(ch); 1516 i = j + 1; 1517 } 1518 else if (ch < WHITESPACE) 1519 { 1520 nodeWriter.WriteChars(chars + i, j - i); 1521 nodeWriter.WriteText(BACK_SLASH); 1522 if (CharacterAbbrevs[ch] == 0) 1523 { 1524 nodeWriter.WriteText('u'); 1525 nodeWriter.WriteText(string.Format(CultureInfo.InvariantCulture, "{0:x4}", (int)ch)); 1526 i = j + 1; 1527 } 1528 else 1529 { 1530 nodeWriter.WriteText(CharacterAbbrevs[ch]); 1531 i = j + 1; 1532 } 1533 } 1534 } 1535 else if (ch == BACK_SLASH) 1536 { 1537 nodeWriter.WriteChars(chars + i, j - i); 1538 nodeWriter.WriteText(BACK_SLASH); 1539 nodeWriter.WriteText(ch); 1540 i = j + 1; 1541 } 1542 else if ((ch >= HIGH_SURROGATE_START && (ch <= LOW_SURROGATE_END || ch >= MAX_CHAR)) || IsUnicodeNewlineCharacter(ch)) 1543 { 1544 nodeWriter.WriteChars(chars + i, j - i); 1545 nodeWriter.WriteText(BACK_SLASH); 1546 nodeWriter.WriteText('u'); 1547 nodeWriter.WriteText(string.Format(CultureInfo.InvariantCulture, "{0:x4}", (int)ch)); 1548 i = j + 1; 1549 } 1550 } 1551 if (i < j) 1552 { 1553 nodeWriter.WriteChars(chars + i, j - i); 1554 } 1555 } 1556 } 1557 WriteIndent()1558 void WriteIndent() 1559 { 1560 for (int i = 0; i < indentLevel; i++) 1561 { 1562 nodeWriter.WriteText(indentChars); 1563 } 1564 } 1565 WriteNewLine()1566 void WriteNewLine() 1567 { 1568 nodeWriter.WriteText(CARRIAGE_RETURN); 1569 nodeWriter.WriteText(NEWLINE); 1570 } 1571 WriteJsonElementName(string localName)1572 void WriteJsonElementName(string localName) 1573 { 1574 WriteJsonQuote(); 1575 WriteEscapedJsonString(localName); 1576 WriteJsonQuote(); 1577 nodeWriter.WriteText(JsonGlobals.NameValueSeparatorChar); 1578 if (indent) 1579 { 1580 nodeWriter.WriteText(WHITESPACE); 1581 } 1582 } 1583 WriteJsonQuote()1584 void WriteJsonQuote() 1585 { 1586 nodeWriter.WriteText(JsonGlobals.QuoteChar); 1587 } 1588 WritePrimitiveValue(object value)1589 void WritePrimitiveValue(object value) 1590 { 1591 if (IsClosed) 1592 { 1593 ThrowClosed(); 1594 } 1595 1596 if (value == null) 1597 { 1598 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("value")); 1599 } 1600 1601 if (value is ulong) 1602 { 1603 WriteValue((ulong)value); 1604 } 1605 else if (value is string) 1606 { 1607 WriteValue((string)value); 1608 } 1609 else if (value is int) 1610 { 1611 WriteValue((int)value); 1612 } 1613 else if (value is long) 1614 { 1615 WriteValue((long)value); 1616 } 1617 else if (value is bool) 1618 { 1619 WriteValue((bool)value); 1620 } 1621 else if (value is double) 1622 { 1623 WriteValue((double)value); 1624 } 1625 else if (value is DateTime) 1626 { 1627 WriteValue((DateTime)value); 1628 } 1629 else if (value is float) 1630 { 1631 WriteValue((float)value); 1632 } 1633 else if (value is decimal) 1634 { 1635 WriteValue((decimal)value); 1636 } 1637 else if (value is XmlDictionaryString) 1638 { 1639 WriteValue((XmlDictionaryString)value); 1640 } 1641 else if (value is UniqueId) 1642 { 1643 WriteValue((UniqueId)value); 1644 } 1645 else if (value is Guid) 1646 { 1647 WriteValue((Guid)value); 1648 } 1649 else if (value is TimeSpan) 1650 { 1651 WriteValue((TimeSpan)value); 1652 } 1653 else if (value.GetType().IsArray) 1654 { 1655 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.JsonNestedArraysNotSupported), "value")); 1656 } 1657 else 1658 { 1659 base.WriteValue(value); 1660 } 1661 } 1662 WriteServerTypeAttribute()1663 void WriteServerTypeAttribute() 1664 { 1665 string value = serverTypeValue; 1666 JsonDataType oldDataType = dataType; 1667 NameState oldNameState = nameState; 1668 WriteStartElement(JsonGlobals.serverTypeString); 1669 WriteValue(value); 1670 WriteEndElement(); 1671 dataType = oldDataType; 1672 nameState = oldNameState; 1673 wroteServerTypeAttribute = true; 1674 } 1675 WriteValue(ulong value)1676 void WriteValue(ulong value) 1677 { 1678 StartText(); 1679 nodeWriter.WriteUInt64Text(value); 1680 } 1681 WriteValue(Array array)1682 void WriteValue(Array array) 1683 { 1684 // This method is called only if WriteValue(object) is called with an array 1685 // The contract for XmlWriter.WriteValue(object) requires that this object array be written out as a string. 1686 // E.g. WriteValue(new int[] { 1, 2, 3}) should be equivalent to WriteString("1 2 3"). 1687 JsonDataType oldDataType = dataType; 1688 // Set attribute mode to String because WritePrimitiveValue might write numerical text. 1689 // Calls to methods that write numbers can't be mixed with calls that write quoted text unless the attribute mode is explictly string. 1690 dataType = JsonDataType.String; 1691 StartText(); 1692 for (int i = 0; i < array.Length; i++) 1693 { 1694 if (i != 0) 1695 { 1696 nodeWriter.WriteText(JsonGlobals.WhitespaceChar); 1697 } 1698 WritePrimitiveValue(array.GetValue(i)); 1699 } 1700 dataType = oldDataType; 1701 } 1702 1703 class JsonNodeWriter : XmlUTF8NodeWriter 1704 { 1705 [SecurityCritical] WriteChars(char* chars, int charCount)1706 internal unsafe void WriteChars(char* chars, int charCount) 1707 { 1708 base.UnsafeWriteUTF8Chars(chars, charCount); 1709 } 1710 } 1711 } 1712 } 1713