1646 lines
62 KiB
C#
1646 lines
62 KiB
C#
|
//------------------------------------------------------------
|
||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
//------------------------------------------------------------
|
||
|
|
||
|
namespace System.Runtime.Serialization.Json
|
||
|
{
|
||
|
using System.Diagnostics.CodeAnalysis;
|
||
|
using System.Globalization;
|
||
|
using System.IO;
|
||
|
using System.Runtime;
|
||
|
using System.Runtime.Serialization;
|
||
|
using System.Security;
|
||
|
using System.ServiceModel;
|
||
|
using System.Text;
|
||
|
using System.Xml;
|
||
|
|
||
|
class XmlJsonWriter : XmlDictionaryWriter, IXmlJsonWriterInitializer
|
||
|
{
|
||
|
const char BACK_SLASH = '\\';
|
||
|
const char FORWARD_SLASH = '/';
|
||
|
|
||
|
const char HIGH_SURROGATE_START = (char)0xd800;
|
||
|
const char LOW_SURROGATE_END = (char)0xdfff;
|
||
|
const char MAX_CHAR = (char)0xfffe;
|
||
|
const char WHITESPACE = ' ';
|
||
|
const char CARRIAGE_RETURN = '\r';
|
||
|
const char NEWLINE = '\n';
|
||
|
const string xmlNamespace = "http://www.w3.org/XML/1998/namespace";
|
||
|
const string xmlnsNamespace = "http://www.w3.org/2000/xmlns/";
|
||
|
|
||
|
[Fx.Tag.SecurityNote(Critical = "Static fields are marked SecurityCritical or readonly to prevent"
|
||
|
+ " data from being modified or leaked to other components in appdomain.")]
|
||
|
[SecurityCritical]
|
||
|
static BinHexEncoding binHexEncoding;
|
||
|
|
||
|
string attributeText;
|
||
|
JsonDataType dataType;
|
||
|
int depth;
|
||
|
bool endElementBuffer;
|
||
|
bool isWritingDataTypeAttribute;
|
||
|
bool isWritingServerTypeAttribute;
|
||
|
bool isWritingXmlnsAttribute;
|
||
|
bool isWritingXmlnsAttributeDefaultNs;
|
||
|
NameState nameState;
|
||
|
JsonNodeType nodeType;
|
||
|
JsonNodeWriter nodeWriter;
|
||
|
JsonNodeType[] scopes;
|
||
|
string serverTypeValue;
|
||
|
// Do not use this field's value anywhere other than the WriteState property.
|
||
|
// It's OK to set this field's value anywhere and then change the WriteState property appropriately.
|
||
|
// If it's necessary to check the WriteState outside WriteState, use the WriteState property.
|
||
|
WriteState writeState;
|
||
|
bool wroteServerTypeAttribute;
|
||
|
bool indent;
|
||
|
string indentChars;
|
||
|
int indentLevel;
|
||
|
|
||
|
public XmlJsonWriter() : this(false, null) { }
|
||
|
|
||
|
public XmlJsonWriter(bool indent, string indentChars)
|
||
|
{
|
||
|
this.indent = indent;
|
||
|
if (indent)
|
||
|
{
|
||
|
if (indentChars == null)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("indentChars");
|
||
|
}
|
||
|
this.indentChars = indentChars;
|
||
|
}
|
||
|
InitializeWriter();
|
||
|
}
|
||
|
|
||
|
enum JsonDataType
|
||
|
{
|
||
|
None,
|
||
|
Null,
|
||
|
Boolean,
|
||
|
Number,
|
||
|
String,
|
||
|
Object,
|
||
|
Array
|
||
|
};
|
||
|
|
||
|
[Flags]
|
||
|
enum NameState
|
||
|
{
|
||
|
None = 0,
|
||
|
IsWritingNameWithMapping = 1,
|
||
|
IsWritingNameAttribute = 2,
|
||
|
WrittenNameWithMapping = 4,
|
||
|
}
|
||
|
|
||
|
public override XmlWriterSettings Settings
|
||
|
{
|
||
|
// The XmlWriterSettings object used to create this writer instance.
|
||
|
// If this writer was not created using the Create method, this property
|
||
|
// returns a null reference.
|
||
|
get { return null; }
|
||
|
}
|
||
|
|
||
|
public override WriteState WriteState
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
if (writeState == WriteState.Closed)
|
||
|
{
|
||
|
return WriteState.Closed;
|
||
|
}
|
||
|
if (HasOpenAttribute)
|
||
|
{
|
||
|
return WriteState.Attribute;
|
||
|
}
|
||
|
switch (nodeType)
|
||
|
{
|
||
|
case JsonNodeType.None:
|
||
|
return WriteState.Start;
|
||
|
case JsonNodeType.Element:
|
||
|
return WriteState.Element;
|
||
|
case JsonNodeType.QuotedText:
|
||
|
case JsonNodeType.StandaloneText:
|
||
|
case JsonNodeType.EndElement:
|
||
|
return WriteState.Content;
|
||
|
default:
|
||
|
return WriteState.Error;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public override string XmlLang
|
||
|
{
|
||
|
get { return null; }
|
||
|
}
|
||
|
|
||
|
public override XmlSpace XmlSpace
|
||
|
{
|
||
|
get { return XmlSpace.None; }
|
||
|
}
|
||
|
|
||
|
static BinHexEncoding BinHexEncoding
|
||
|
{
|
||
|
[Fx.Tag.SecurityNote(Critical = "Fetches the critical binHexEncoding field.",
|
||
|
Safe = "Get-only properties only need to be protected for write; initialized in getter if null.")]
|
||
|
[SecuritySafeCritical]
|
||
|
get
|
||
|
{
|
||
|
if (binHexEncoding == null)
|
||
|
{
|
||
|
binHexEncoding = new BinHexEncoding();
|
||
|
}
|
||
|
return binHexEncoding;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool HasOpenAttribute
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return (isWritingDataTypeAttribute || isWritingServerTypeAttribute || IsWritingNameAttribute || isWritingXmlnsAttribute);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool IsClosed
|
||
|
{
|
||
|
get { return (WriteState == WriteState.Closed); }
|
||
|
}
|
||
|
|
||
|
bool IsWritingCollection
|
||
|
{
|
||
|
get { return (depth > 0) && (scopes[depth] == JsonNodeType.Collection); }
|
||
|
}
|
||
|
|
||
|
bool IsWritingNameAttribute
|
||
|
{
|
||
|
get { return (nameState & NameState.IsWritingNameAttribute) == NameState.IsWritingNameAttribute; }
|
||
|
}
|
||
|
|
||
|
bool IsWritingNameWithMapping
|
||
|
{
|
||
|
get { return (nameState & NameState.IsWritingNameWithMapping) == NameState.IsWritingNameWithMapping; }
|
||
|
}
|
||
|
|
||
|
bool WrittenNameWithMapping
|
||
|
{
|
||
|
get { return (nameState & NameState.WrittenNameWithMapping) == NameState.WrittenNameWithMapping; }
|
||
|
}
|
||
|
|
||
|
public override void Close()
|
||
|
{
|
||
|
if (!IsClosed)
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
WriteEndDocument();
|
||
|
}
|
||
|
finally
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
nodeWriter.Flush();
|
||
|
nodeWriter.Close();
|
||
|
}
|
||
|
finally
|
||
|
{
|
||
|
writeState = WriteState.Closed;
|
||
|
if (depth != 0)
|
||
|
{
|
||
|
depth = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public override void Flush()
|
||
|
{
|
||
|
if (IsClosed)
|
||
|
{
|
||
|
ThrowClosed();
|
||
|
}
|
||
|
nodeWriter.Flush();
|
||
|
}
|
||
|
|
||
|
public override string LookupPrefix(string ns)
|
||
|
{
|
||
|
if (ns == null)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("ns");
|
||
|
}
|
||
|
if (ns == Globals.XmlnsNamespace)
|
||
|
{
|
||
|
return Globals.XmlnsPrefix;
|
||
|
}
|
||
|
if (ns == xmlNamespace)
|
||
|
{
|
||
|
return JsonGlobals.xmlPrefix;
|
||
|
}
|
||
|
if (ns == string.Empty)
|
||
|
{
|
||
|
return string.Empty;
|
||
|
}
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
public void SetOutput(Stream stream, Encoding encoding, bool ownsStream)
|
||
|
{
|
||
|
if (stream == null)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("stream");
|
||
|
}
|
||
|
if (encoding == null)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("encoding");
|
||
|
}
|
||
|
if (encoding.WebName != Encoding.UTF8.WebName)
|
||
|
{
|
||
|
stream = new JsonEncodingStreamWrapper(stream, encoding, false);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
encoding = null;
|
||
|
}
|
||
|
if (nodeWriter == null)
|
||
|
{
|
||
|
nodeWriter = new JsonNodeWriter();
|
||
|
}
|
||
|
|
||
|
nodeWriter.SetOutput(stream, ownsStream, encoding);
|
||
|
InitializeWriter();
|
||
|
}
|
||
|
|
||
|
public override void WriteArray(string prefix, string localName, string namespaceUri, bool[] array, int offset, int count)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.JsonWriteArrayNotSupported)));
|
||
|
}
|
||
|
|
||
|
public override void WriteArray(string prefix, string localName, string namespaceUri, Int16[] array, int offset, int count)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.JsonWriteArrayNotSupported)));
|
||
|
}
|
||
|
|
||
|
public override void WriteArray(string prefix, string localName, string namespaceUri, Int32[] array, int offset, int count)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.JsonWriteArrayNotSupported)));
|
||
|
}
|
||
|
|
||
|
public override void WriteArray(string prefix, string localName, string namespaceUri, Int64[] array, int offset, int count)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.JsonWriteArrayNotSupported)));
|
||
|
}
|
||
|
|
||
|
public override void WriteArray(string prefix, string localName, string namespaceUri, float[] array, int offset, int count)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.JsonWriteArrayNotSupported)));
|
||
|
}
|
||
|
|
||
|
public override void WriteArray(string prefix, string localName, string namespaceUri, double[] array, int offset, int count)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.JsonWriteArrayNotSupported)));
|
||
|
}
|
||
|
|
||
|
public override void WriteArray(string prefix, string localName, string namespaceUri, decimal[] array, int offset, int count)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.JsonWriteArrayNotSupported)));
|
||
|
}
|
||
|
|
||
|
public override void WriteArray(string prefix, string localName, string namespaceUri, DateTime[] array, int offset, int count)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.JsonWriteArrayNotSupported)));
|
||
|
}
|
||
|
|
||
|
public override void WriteArray(string prefix, string localName, string namespaceUri, Guid[] array, int offset, int count)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.JsonWriteArrayNotSupported)));
|
||
|
}
|
||
|
|
||
|
public override void WriteArray(string prefix, string localName, string namespaceUri, TimeSpan[] array, int offset, int count)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.JsonWriteArrayNotSupported)));
|
||
|
}
|
||
|
|
||
|
public override void WriteArray(string prefix, XmlDictionaryString localName, XmlDictionaryString namespaceUri, bool[] array, int offset, int count)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.JsonWriteArrayNotSupported)));
|
||
|
}
|
||
|
|
||
|
public override void WriteArray(string prefix, XmlDictionaryString localName, XmlDictionaryString namespaceUri, decimal[] array, int offset, int count)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.JsonWriteArrayNotSupported)));
|
||
|
}
|
||
|
|
||
|
public override void WriteArray(string prefix, XmlDictionaryString localName, XmlDictionaryString namespaceUri, double[] array, int offset, int count)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.JsonWriteArrayNotSupported)));
|
||
|
}
|
||
|
|
||
|
public override void WriteArray(string prefix, XmlDictionaryString localName, XmlDictionaryString namespaceUri, float[] array, int offset, int count)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.JsonWriteArrayNotSupported)));
|
||
|
}
|
||
|
|
||
|
public override void WriteArray(string prefix, XmlDictionaryString localName, XmlDictionaryString namespaceUri, int[] array, int offset, int count)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.JsonWriteArrayNotSupported)));
|
||
|
}
|
||
|
|
||
|
public override void WriteArray(string prefix, XmlDictionaryString localName, XmlDictionaryString namespaceUri, long[] array, int offset, int count)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.JsonWriteArrayNotSupported)));
|
||
|
}
|
||
|
|
||
|
public override void WriteArray(string prefix, XmlDictionaryString localName, XmlDictionaryString namespaceUri, short[] array, int offset, int count)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.JsonWriteArrayNotSupported)));
|
||
|
}
|
||
|
|
||
|
public override void WriteArray(string prefix, XmlDictionaryString localName, XmlDictionaryString namespaceUri, DateTime[] array, int offset, int count)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.JsonWriteArrayNotSupported)));
|
||
|
}
|
||
|
|
||
|
public override void WriteArray(string prefix, XmlDictionaryString localName, XmlDictionaryString namespaceUri, Guid[] array, int offset, int count)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.JsonWriteArrayNotSupported)));
|
||
|
}
|
||
|
|
||
|
public override void WriteArray(string prefix, XmlDictionaryString localName, XmlDictionaryString namespaceUri, TimeSpan[] array, int offset, int count)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.JsonWriteArrayNotSupported)));
|
||
|
}
|
||
|
|
||
|
public override void WriteBase64(byte[] buffer, int index, int count)
|
||
|
{
|
||
|
if (buffer == null)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("buffer");
|
||
|
}
|
||
|
|
||
|
// Not checking upper bound because it will be caught by "count". This is what XmlTextWriter does.
|
||
|
if (index < 0)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
||
|
new ArgumentOutOfRangeException("index", SR.GetString(SR.ValueMustBeNonNegative)));
|
||
|
}
|
||
|
|
||
|
if (count < 0)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
||
|
new ArgumentOutOfRangeException("count", SR.GetString(SR.ValueMustBeNonNegative)));
|
||
|
}
|
||
|
if (count > buffer.Length - index)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
||
|
new ArgumentOutOfRangeException("count",
|
||
|
SR.GetString(SR.JsonSizeExceedsRemainingBufferSpace,
|
||
|
buffer.Length - index)));
|
||
|
}
|
||
|
|
||
|
StartText();
|
||
|
nodeWriter.WriteBase64Text(buffer, 0, buffer, index, count);
|
||
|
}
|
||
|
|
||
|
public override void WriteBinHex(byte[] buffer, int index, int count)
|
||
|
{
|
||
|
if (buffer == null)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("buffer");
|
||
|
}
|
||
|
|
||
|
// Not checking upper bound because it will be caught by "count". This is what XmlTextWriter does.
|
||
|
if (index < 0)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
||
|
new ArgumentOutOfRangeException("index", SR.GetString(SR.ValueMustBeNonNegative)));
|
||
|
}
|
||
|
|
||
|
if (count < 0)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
||
|
new ArgumentOutOfRangeException("count", SR.GetString(SR.ValueMustBeNonNegative)));
|
||
|
}
|
||
|
if (count > buffer.Length - index)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
||
|
new ArgumentOutOfRangeException("count",
|
||
|
SR.GetString(SR.JsonSizeExceedsRemainingBufferSpace,
|
||
|
buffer.Length - index)));
|
||
|
}
|
||
|
|
||
|
StartText();
|
||
|
WriteEscapedJsonString(BinHexEncoding.GetString(buffer, index, count));
|
||
|
}
|
||
|
|
||
|
public override void WriteCData(string text)
|
||
|
{
|
||
|
WriteString(text);
|
||
|
}
|
||
|
|
||
|
public override void WriteCharEntity(char ch)
|
||
|
{
|
||
|
WriteString(ch.ToString());
|
||
|
}
|
||
|
|
||
|
public override void WriteChars(char[] buffer, int index, int count)
|
||
|
{
|
||
|
if (buffer == null)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("buffer");
|
||
|
}
|
||
|
|
||
|
// Not checking upper bound because it will be caught by "count". This is what XmlTextWriter does.
|
||
|
if (index < 0)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
||
|
new ArgumentOutOfRangeException("index", SR.GetString(SR.ValueMustBeNonNegative)));
|
||
|
}
|
||
|
|
||
|
if (count < 0)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
||
|
new ArgumentOutOfRangeException("count", SR.GetString(SR.ValueMustBeNonNegative)));
|
||
|
}
|
||
|
if (count > buffer.Length - index)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
||
|
new ArgumentOutOfRangeException("count",
|
||
|
SR.GetString(SR.JsonSizeExceedsRemainingBufferSpace,
|
||
|
buffer.Length - index)));
|
||
|
}
|
||
|
|
||
|
WriteString(new string(buffer, index, count));
|
||
|
}
|
||
|
|
||
|
public override void WriteComment(string text)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.JsonMethodNotSupported, "WriteComment")));
|
||
|
}
|
||
|
|
||
|
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "2#sysid", Justification = "This method is derived from the base")]
|
||
|
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "1#pubid", Justification = "This method is derived from the base")]
|
||
|
public override void WriteDocType(string name, string pubid, string sysid, string subset)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.JsonMethodNotSupported, "WriteDocType")));
|
||
|
}
|
||
|
|
||
|
public override void WriteEndAttribute()
|
||
|
{
|
||
|
if (IsClosed)
|
||
|
{
|
||
|
ThrowClosed();
|
||
|
}
|
||
|
if (!HasOpenAttribute)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
||
|
new XmlException(SR.GetString(SR.JsonNoMatchingStartAttribute)));
|
||
|
}
|
||
|
|
||
|
Fx.Assert(!(isWritingDataTypeAttribute && isWritingServerTypeAttribute),
|
||
|
"Can not write type attribute and __type attribute at the same time.");
|
||
|
|
||
|
if (isWritingDataTypeAttribute)
|
||
|
{
|
||
|
switch (attributeText)
|
||
|
{
|
||
|
case JsonGlobals.numberString:
|
||
|
{
|
||
|
ThrowIfServerTypeWritten(JsonGlobals.numberString);
|
||
|
dataType = JsonDataType.Number;
|
||
|
break;
|
||
|
}
|
||
|
case JsonGlobals.stringString:
|
||
|
{
|
||
|
ThrowIfServerTypeWritten(JsonGlobals.stringString);
|
||
|
dataType = JsonDataType.String;
|
||
|
break;
|
||
|
}
|
||
|
case JsonGlobals.arrayString:
|
||
|
{
|
||
|
ThrowIfServerTypeWritten(JsonGlobals.arrayString);
|
||
|
dataType = JsonDataType.Array;
|
||
|
break;
|
||
|
}
|
||
|
case JsonGlobals.objectString:
|
||
|
{
|
||
|
dataType = JsonDataType.Object;
|
||
|
break;
|
||
|
}
|
||
|
case JsonGlobals.nullString:
|
||
|
{
|
||
|
ThrowIfServerTypeWritten(JsonGlobals.nullString);
|
||
|
dataType = JsonDataType.Null;
|
||
|
break;
|
||
|
}
|
||
|
case JsonGlobals.booleanString:
|
||
|
{
|
||
|
ThrowIfServerTypeWritten(JsonGlobals.booleanString);
|
||
|
dataType = JsonDataType.Boolean;
|
||
|
break;
|
||
|
}
|
||
|
default:
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
||
|
new XmlException(SR.GetString(SR.JsonUnexpectedAttributeValue, attributeText)));
|
||
|
}
|
||
|
|
||
|
attributeText = null;
|
||
|
isWritingDataTypeAttribute = false;
|
||
|
|
||
|
if (!IsWritingNameWithMapping || WrittenNameWithMapping)
|
||
|
{
|
||
|
WriteDataTypeServerType();
|
||
|
}
|
||
|
}
|
||
|
else if (isWritingServerTypeAttribute)
|
||
|
{
|
||
|
serverTypeValue = attributeText;
|
||
|
attributeText = null;
|
||
|
isWritingServerTypeAttribute = false;
|
||
|
|
||
|
// we are writing __type after type="object" (enforced by WSE)
|
||
|
if ((!IsWritingNameWithMapping || WrittenNameWithMapping) && dataType == JsonDataType.Object)
|
||
|
{
|
||
|
WriteServerTypeAttribute();
|
||
|
}
|
||
|
}
|
||
|
else if (IsWritingNameAttribute)
|
||
|
{
|
||
|
WriteJsonElementName(attributeText);
|
||
|
attributeText = null;
|
||
|
nameState = NameState.IsWritingNameWithMapping | NameState.WrittenNameWithMapping;
|
||
|
WriteDataTypeServerType();
|
||
|
}
|
||
|
else if (isWritingXmlnsAttribute)
|
||
|
{
|
||
|
if (!string.IsNullOrEmpty(attributeText) && isWritingXmlnsAttributeDefaultNs)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("ns", SR.GetString(SR.JsonNamespaceMustBeEmpty, attributeText));
|
||
|
}
|
||
|
|
||
|
attributeText = null;
|
||
|
isWritingXmlnsAttribute = false;
|
||
|
isWritingXmlnsAttributeDefaultNs = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public override void WriteEndDocument()
|
||
|
{
|
||
|
if (IsClosed)
|
||
|
{
|
||
|
ThrowClosed();
|
||
|
}
|
||
|
if (nodeType != JsonNodeType.None)
|
||
|
{
|
||
|
while (depth > 0)
|
||
|
{
|
||
|
WriteEndElement();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public override void WriteEndElement()
|
||
|
{
|
||
|
if (IsClosed)
|
||
|
{
|
||
|
ThrowClosed();
|
||
|
}
|
||
|
|
||
|
if (depth == 0)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
||
|
new XmlException(SR.GetString(SR.JsonEndElementNoOpenNodes)));
|
||
|
}
|
||
|
if (HasOpenAttribute)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
||
|
new XmlException(SR.GetString(SR.JsonOpenAttributeMustBeClosedFirst, "WriteEndElement")));
|
||
|
}
|
||
|
|
||
|
endElementBuffer = false;
|
||
|
|
||
|
JsonNodeType token = ExitScope();
|
||
|
if (token == JsonNodeType.Collection)
|
||
|
{
|
||
|
indentLevel--;
|
||
|
if (indent)
|
||
|
{
|
||
|
if (nodeType == JsonNodeType.Element)
|
||
|
{
|
||
|
nodeWriter.WriteText(WHITESPACE);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
WriteNewLine();
|
||
|
WriteIndent();
|
||
|
}
|
||
|
}
|
||
|
nodeWriter.WriteText(JsonGlobals.EndCollectionChar);
|
||
|
token = ExitScope();
|
||
|
}
|
||
|
else if (nodeType == JsonNodeType.QuotedText)
|
||
|
{
|
||
|
// For writing "
|
||
|
WriteJsonQuote();
|
||
|
}
|
||
|
else if (nodeType == JsonNodeType.Element)
|
||
|
{
|
||
|
if ((dataType == JsonDataType.None) && (serverTypeValue != null))
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
||
|
new XmlException(SR.GetString(SR.JsonMustSpecifyDataType,
|
||
|
JsonGlobals.typeString, JsonGlobals.objectString, JsonGlobals.serverTypeString)));
|
||
|
}
|
||
|
|
||
|
if (IsWritingNameWithMapping && !WrittenNameWithMapping)
|
||
|
{
|
||
|
// Ending </item> without writing item attribute
|
||
|
// Not providing a better error message because localization deadline has passed.
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
||
|
new XmlException(SR.GetString(SR.JsonMustSpecifyDataType,
|
||
|
JsonGlobals.itemString, string.Empty, JsonGlobals.itemString)));
|
||
|
}
|
||
|
|
||
|
// the element is empty, it does not have any content,
|
||
|
if ((dataType == JsonDataType.None) ||
|
||
|
(dataType == JsonDataType.String))
|
||
|
{
|
||
|
nodeWriter.WriteText(JsonGlobals.QuoteChar);
|
||
|
nodeWriter.WriteText(JsonGlobals.QuoteChar);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Assert on only StandaloneText and EndElement because preceding if
|
||
|
// conditions take care of checking for QuotedText and Element.
|
||
|
Fx.Assert((nodeType == JsonNodeType.StandaloneText) || (nodeType == JsonNodeType.EndElement),
|
||
|
"nodeType has invalid value " + nodeType + ". Expected it to be QuotedText, Element, StandaloneText, or EndElement.");
|
||
|
}
|
||
|
if (depth != 0)
|
||
|
{
|
||
|
if (token == JsonNodeType.Element)
|
||
|
{
|
||
|
endElementBuffer = true;
|
||
|
}
|
||
|
else if (token == JsonNodeType.Object)
|
||
|
{
|
||
|
indentLevel--;
|
||
|
if (indent)
|
||
|
{
|
||
|
if (nodeType == JsonNodeType.Element)
|
||
|
{
|
||
|
nodeWriter.WriteText(WHITESPACE);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
WriteNewLine();
|
||
|
WriteIndent();
|
||
|
}
|
||
|
}
|
||
|
nodeWriter.WriteText(JsonGlobals.EndObjectChar);
|
||
|
if ((depth > 0) && scopes[depth] == JsonNodeType.Element)
|
||
|
{
|
||
|
ExitScope();
|
||
|
endElementBuffer = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
dataType = JsonDataType.None;
|
||
|
nodeType = JsonNodeType.EndElement;
|
||
|
nameState = NameState.None;
|
||
|
wroteServerTypeAttribute = false;
|
||
|
}
|
||
|
|
||
|
public override void WriteEntityRef(string name)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.JsonMethodNotSupported, "WriteEntityRef")));
|
||
|
}
|
||
|
|
||
|
public override void WriteFullEndElement()
|
||
|
{
|
||
|
WriteEndElement();
|
||
|
}
|
||
|
|
||
|
public override void WriteProcessingInstruction(string name, string text)
|
||
|
{
|
||
|
if (IsClosed)
|
||
|
{
|
||
|
ThrowClosed();
|
||
|
}
|
||
|
|
||
|
if (!name.Equals("xml", StringComparison.OrdinalIgnoreCase))
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.JsonXmlProcessingInstructionNotSupported), "name"));
|
||
|
}
|
||
|
|
||
|
if (WriteState != WriteState.Start)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.JsonXmlInvalidDeclaration)));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public override void WriteQualifiedName(string localName, string ns)
|
||
|
{
|
||
|
if (localName == null)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("localName");
|
||
|
}
|
||
|
if (localName.Length == 0)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("localName",
|
||
|
SR.GetString(SR.JsonInvalidLocalNameEmpty));
|
||
|
}
|
||
|
if (ns == null)
|
||
|
{
|
||
|
ns = string.Empty;
|
||
|
}
|
||
|
|
||
|
base.WriteQualifiedName(localName, ns);
|
||
|
}
|
||
|
|
||
|
public override void WriteRaw(string data)
|
||
|
{
|
||
|
WriteString(data);
|
||
|
}
|
||
|
|
||
|
public override void WriteRaw(char[] buffer, int index, int count)
|
||
|
{
|
||
|
if (buffer == null)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("buffer");
|
||
|
}
|
||
|
|
||
|
// Not checking upper bound because it will be caught by "count". This is what XmlTextWriter does.
|
||
|
if (index < 0)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
||
|
new ArgumentOutOfRangeException("index", SR.GetString(SR.ValueMustBeNonNegative)));
|
||
|
}
|
||
|
|
||
|
if (count < 0)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
||
|
new ArgumentOutOfRangeException("count", SR.GetString(SR.ValueMustBeNonNegative)));
|
||
|
}
|
||
|
if (count > buffer.Length - index)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
||
|
new ArgumentOutOfRangeException("count",
|
||
|
SR.GetString(SR.JsonSizeExceedsRemainingBufferSpace,
|
||
|
buffer.Length - index)));
|
||
|
}
|
||
|
|
||
|
WriteString(new string(buffer, index, count));
|
||
|
}
|
||
|
|
||
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase")] // [....], ToLowerInvariant is just used in Json error message
|
||
|
public override void WriteStartAttribute(string prefix, string localName, string ns)
|
||
|
{
|
||
|
if (IsClosed)
|
||
|
{
|
||
|
ThrowClosed();
|
||
|
}
|
||
|
if (!string.IsNullOrEmpty(prefix))
|
||
|
{
|
||
|
if (IsWritingNameWithMapping && prefix == JsonGlobals.xmlnsPrefix)
|
||
|
{
|
||
|
if (ns != null && ns != xmlnsNamespace)
|
||
|
{
|
||
|
throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(System.Runtime.Serialization.SR.GetString(System.Runtime.Serialization.SR.XmlPrefixBoundToNamespace, "xmlns", xmlnsNamespace, ns), "ns"));
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("prefix", SR.GetString(SR.JsonPrefixMustBeNullOrEmpty, prefix));
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (IsWritingNameWithMapping && ns == xmlnsNamespace && localName != JsonGlobals.xmlnsPrefix)
|
||
|
{
|
||
|
prefix = JsonGlobals.xmlnsPrefix;
|
||
|
}
|
||
|
}
|
||
|
if (!string.IsNullOrEmpty(ns))
|
||
|
{
|
||
|
if (IsWritingNameWithMapping && ns == xmlnsNamespace)
|
||
|
{
|
||
|
prefix = JsonGlobals.xmlnsPrefix;
|
||
|
}
|
||
|
else if (string.IsNullOrEmpty(prefix) && localName == JsonGlobals.xmlnsPrefix && ns == xmlnsNamespace)
|
||
|
{
|
||
|
prefix = JsonGlobals.xmlnsPrefix;
|
||
|
isWritingXmlnsAttributeDefaultNs = true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("ns", SR.GetString(SR.JsonNamespaceMustBeEmpty, ns));
|
||
|
}
|
||
|
}
|
||
|
if (localName == null)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("localName");
|
||
|
}
|
||
|
if (localName.Length == 0)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("localName", SR.GetString(SR.JsonInvalidLocalNameEmpty));
|
||
|
}
|
||
|
if ((nodeType != JsonNodeType.Element) && !wroteServerTypeAttribute)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.JsonAttributeMustHaveElement)));
|
||
|
}
|
||
|
if (HasOpenAttribute)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
||
|
new XmlException(SR.GetString(SR.JsonOpenAttributeMustBeClosedFirst, "WriteStartAttribute")));
|
||
|
}
|
||
|
if (prefix == JsonGlobals.xmlnsPrefix)
|
||
|
{
|
||
|
isWritingXmlnsAttribute = true;
|
||
|
}
|
||
|
else if (localName == JsonGlobals.typeString)
|
||
|
{
|
||
|
if (dataType != JsonDataType.None)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
||
|
new XmlException(SR.GetString(SR.JsonAttributeAlreadyWritten, JsonGlobals.typeString)));
|
||
|
}
|
||
|
|
||
|
isWritingDataTypeAttribute = true;
|
||
|
}
|
||
|
else if (localName == JsonGlobals.serverTypeString)
|
||
|
{
|
||
|
if (serverTypeValue != null)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
||
|
new XmlException(SR.GetString(SR.JsonAttributeAlreadyWritten, JsonGlobals.serverTypeString)));
|
||
|
}
|
||
|
|
||
|
if ((dataType != JsonDataType.None) && (dataType != JsonDataType.Object))
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
||
|
new XmlException(SR.GetString(SR.JsonServerTypeSpecifiedForInvalidDataType,
|
||
|
JsonGlobals.serverTypeString, JsonGlobals.typeString, dataType.ToString().ToLowerInvariant(), JsonGlobals.objectString)));
|
||
|
}
|
||
|
|
||
|
isWritingServerTypeAttribute = true;
|
||
|
}
|
||
|
else if (localName == JsonGlobals.itemString)
|
||
|
{
|
||
|
if (WrittenNameWithMapping)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
||
|
new XmlException(SR.GetString(SR.JsonAttributeAlreadyWritten, JsonGlobals.itemString)));
|
||
|
}
|
||
|
|
||
|
if (!IsWritingNameWithMapping)
|
||
|
{
|
||
|
// Don't write attribute with local name "item" if <item> element is not open.
|
||
|
// Not providing a better error message because localization deadline has passed.
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
||
|
new XmlException(SR.GetString(SR.JsonEndElementNoOpenNodes)));
|
||
|
}
|
||
|
|
||
|
nameState |= NameState.IsWritingNameAttribute;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("localName", SR.GetString(SR.JsonUnexpectedAttributeLocalName, localName));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public override void WriteStartDocument(bool standalone)
|
||
|
{
|
||
|
// In XML, writes the XML declaration with the version "1.0" and the standalone attribute.
|
||
|
WriteStartDocument();
|
||
|
}
|
||
|
|
||
|
public override void WriteStartDocument()
|
||
|
{
|
||
|
// In XML, writes the XML declaration with the version "1.0".
|
||
|
if (IsClosed)
|
||
|
{
|
||
|
ThrowClosed();
|
||
|
}
|
||
|
if (WriteState != WriteState.Start)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
||
|
new XmlException(
|
||
|
SR.GetString(SR.JsonInvalidWriteState, "WriteStartDocument", WriteState.ToString())));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public override void WriteStartElement(string prefix, string localName, string ns)
|
||
|
{
|
||
|
if (localName == null)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("localName");
|
||
|
}
|
||
|
if (localName.Length == 0)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("localName",
|
||
|
SR.GetString(SR.JsonInvalidLocalNameEmpty));
|
||
|
}
|
||
|
if (!string.IsNullOrEmpty(prefix))
|
||
|
{
|
||
|
if (string.IsNullOrEmpty(ns) || !TrySetWritingNameWithMapping(localName, ns))
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("prefix", SR.GetString(SR.JsonPrefixMustBeNullOrEmpty, prefix));
|
||
|
}
|
||
|
}
|
||
|
if (!string.IsNullOrEmpty(ns))
|
||
|
{
|
||
|
if (!TrySetWritingNameWithMapping(localName, ns))
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("ns", SR.GetString(SR.JsonNamespaceMustBeEmpty, ns));
|
||
|
}
|
||
|
}
|
||
|
if (IsClosed)
|
||
|
{
|
||
|
ThrowClosed();
|
||
|
}
|
||
|
if (HasOpenAttribute)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
||
|
new XmlException(SR.GetString(SR.JsonOpenAttributeMustBeClosedFirst, "WriteStartElement")));
|
||
|
}
|
||
|
if ((nodeType != JsonNodeType.None) && depth == 0)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
||
|
new XmlException(SR.GetString(SR.JsonMultipleRootElementsNotAllowedOnWriter)));
|
||
|
}
|
||
|
|
||
|
switch (nodeType)
|
||
|
{
|
||
|
case JsonNodeType.None:
|
||
|
{
|
||
|
if (!localName.Equals(JsonGlobals.rootString))
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
||
|
new XmlException(SR.GetString(SR.JsonInvalidRootElementName, localName, JsonGlobals.rootString)));
|
||
|
}
|
||
|
EnterScope(JsonNodeType.Element);
|
||
|
break;
|
||
|
}
|
||
|
case JsonNodeType.Element:
|
||
|
{
|
||
|
if ((dataType != JsonDataType.Array) && (dataType != JsonDataType.Object))
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
||
|
new XmlException(SR.GetString(SR.JsonNodeTypeArrayOrObjectNotSpecified)));
|
||
|
}
|
||
|
if (indent)
|
||
|
{
|
||
|
WriteNewLine();
|
||
|
WriteIndent();
|
||
|
}
|
||
|
if (!IsWritingCollection)
|
||
|
{
|
||
|
if (nameState != NameState.IsWritingNameWithMapping)
|
||
|
{
|
||
|
WriteJsonElementName(localName);
|
||
|
}
|
||
|
}
|
||
|
else if (!localName.Equals(JsonGlobals.itemString))
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
||
|
new XmlException(SR.GetString(SR.JsonInvalidItemNameForArrayElement, localName, JsonGlobals.itemString)));
|
||
|
}
|
||
|
EnterScope(JsonNodeType.Element);
|
||
|
break;
|
||
|
}
|
||
|
case JsonNodeType.EndElement:
|
||
|
{
|
||
|
if (endElementBuffer)
|
||
|
{
|
||
|
nodeWriter.WriteText(JsonGlobals.MemberSeparatorChar);
|
||
|
}
|
||
|
if (indent)
|
||
|
{
|
||
|
WriteNewLine();
|
||
|
WriteIndent();
|
||
|
}
|
||
|
if (!IsWritingCollection)
|
||
|
{
|
||
|
if (nameState != NameState.IsWritingNameWithMapping)
|
||
|
{
|
||
|
WriteJsonElementName(localName);
|
||
|
}
|
||
|
}
|
||
|
else if (!localName.Equals(JsonGlobals.itemString))
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
||
|
new XmlException(SR.GetString(SR.JsonInvalidItemNameForArrayElement, localName, JsonGlobals.itemString)));
|
||
|
}
|
||
|
EnterScope(JsonNodeType.Element);
|
||
|
break;
|
||
|
}
|
||
|
default:
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
||
|
new XmlException(SR.GetString(SR.JsonInvalidStartElementCall)));
|
||
|
}
|
||
|
|
||
|
isWritingDataTypeAttribute = false;
|
||
|
isWritingServerTypeAttribute = false;
|
||
|
isWritingXmlnsAttribute = false;
|
||
|
wroteServerTypeAttribute = false;
|
||
|
serverTypeValue = null;
|
||
|
dataType = JsonDataType.None;
|
||
|
nodeType = JsonNodeType.Element;
|
||
|
}
|
||
|
|
||
|
public override void WriteString(string text)
|
||
|
{
|
||
|
if (HasOpenAttribute && (text != null))
|
||
|
{
|
||
|
attributeText += text;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (text == null)
|
||
|
{
|
||
|
text = string.Empty;
|
||
|
}
|
||
|
|
||
|
// do work only when not indenting whitespaces
|
||
|
if (!((this.dataType == JsonDataType.Array || this.dataType == JsonDataType.Object || this.nodeType == JsonNodeType.EndElement) && XmlConverter.IsWhitespace(text)))
|
||
|
{
|
||
|
StartText();
|
||
|
WriteEscapedJsonString(text);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public override void WriteSurrogateCharEntity(char lowChar, char highChar)
|
||
|
{
|
||
|
WriteString(string.Concat(highChar, lowChar));
|
||
|
}
|
||
|
|
||
|
public override void WriteValue(bool value)
|
||
|
{
|
||
|
StartText();
|
||
|
nodeWriter.WriteBoolText(value);
|
||
|
}
|
||
|
|
||
|
public override void WriteValue(decimal value)
|
||
|
{
|
||
|
StartText();
|
||
|
nodeWriter.WriteDecimalText(value);
|
||
|
}
|
||
|
|
||
|
public override void WriteValue(double value)
|
||
|
{
|
||
|
StartText();
|
||
|
nodeWriter.WriteDoubleText(value);
|
||
|
}
|
||
|
|
||
|
public override void WriteValue(float value)
|
||
|
{
|
||
|
StartText();
|
||
|
nodeWriter.WriteFloatText(value);
|
||
|
}
|
||
|
|
||
|
public override void WriteValue(int value)
|
||
|
{
|
||
|
StartText();
|
||
|
nodeWriter.WriteInt32Text(value);
|
||
|
}
|
||
|
|
||
|
public override void WriteValue(long value)
|
||
|
{
|
||
|
StartText();
|
||
|
nodeWriter.WriteInt64Text(value);
|
||
|
}
|
||
|
|
||
|
public override void WriteValue(Guid value)
|
||
|
{
|
||
|
StartText();
|
||
|
nodeWriter.WriteGuidText(value);
|
||
|
}
|
||
|
|
||
|
public override void WriteValue(DateTime value)
|
||
|
{
|
||
|
StartText();
|
||
|
nodeWriter.WriteDateTimeText(value);
|
||
|
}
|
||
|
|
||
|
public override void WriteValue(string value)
|
||
|
{
|
||
|
WriteString(value);
|
||
|
}
|
||
|
|
||
|
public override void WriteValue(TimeSpan value)
|
||
|
{
|
||
|
StartText();
|
||
|
nodeWriter.WriteTimeSpanText(value);
|
||
|
}
|
||
|
|
||
|
public override void WriteValue(UniqueId value)
|
||
|
{
|
||
|
if (value == null)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value");
|
||
|
}
|
||
|
|
||
|
StartText();
|
||
|
nodeWriter.WriteUniqueIdText(value);
|
||
|
}
|
||
|
|
||
|
public override void WriteValue(object value)
|
||
|
{
|
||
|
if (IsClosed)
|
||
|
{
|
||
|
ThrowClosed();
|
||
|
}
|
||
|
|
||
|
if (value == null)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value");
|
||
|
}
|
||
|
|
||
|
if (value is Array)
|
||
|
{
|
||
|
WriteValue((Array)value);
|
||
|
}
|
||
|
else if (value is IStreamProvider)
|
||
|
{
|
||
|
WriteValue((IStreamProvider)value);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
WritePrimitiveValue(value);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "Whitespace", Justification = "This method is derived from the base")]
|
||
|
public override void WriteWhitespace(string ws)
|
||
|
{
|
||
|
if (IsClosed)
|
||
|
{
|
||
|
ThrowClosed();
|
||
|
}
|
||
|
if (ws == null)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("ws");
|
||
|
}
|
||
|
|
||
|
for (int i = 0; i < ws.Length; ++i)
|
||
|
{
|
||
|
char c = ws[i];
|
||
|
if (c != ' ' &&
|
||
|
c != '\t' &&
|
||
|
c != '\n' &&
|
||
|
c != '\r')
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("ws",
|
||
|
SR.GetString(SR.JsonOnlyWhitespace, c.ToString(), "WriteWhitespace"));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
WriteString(ws);
|
||
|
}
|
||
|
|
||
|
public override void WriteXmlAttribute(string localName, string value)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.JsonMethodNotSupported, "WriteXmlAttribute")));
|
||
|
}
|
||
|
|
||
|
public override void WriteXmlAttribute(XmlDictionaryString localName, XmlDictionaryString value)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.JsonMethodNotSupported, "WriteXmlAttribute")));
|
||
|
}
|
||
|
|
||
|
public override void WriteXmlnsAttribute(string prefix, string namespaceUri)
|
||
|
{
|
||
|
if (!IsWritingNameWithMapping)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.JsonMethodNotSupported, "WriteXmlnsAttribute")));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public override void WriteXmlnsAttribute(string prefix, XmlDictionaryString namespaceUri)
|
||
|
{
|
||
|
if (!IsWritingNameWithMapping)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.JsonMethodNotSupported, "WriteXmlnsAttribute")));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal static bool CharacterNeedsEscaping(char ch)
|
||
|
{
|
||
|
return (ch == FORWARD_SLASH || ch == JsonGlobals.QuoteChar || ch < WHITESPACE || ch == BACK_SLASH
|
||
|
|| (ch >= HIGH_SURROGATE_START && (ch <= LOW_SURROGATE_END || ch >= MAX_CHAR)));
|
||
|
}
|
||
|
|
||
|
|
||
|
static void ThrowClosed()
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
||
|
new InvalidOperationException(SR.GetString(SR.JsonWriterClosed)));
|
||
|
}
|
||
|
|
||
|
void CheckText(JsonNodeType nextNodeType)
|
||
|
{
|
||
|
if (IsClosed)
|
||
|
{
|
||
|
ThrowClosed();
|
||
|
}
|
||
|
if (depth == 0)
|
||
|
{
|
||
|
throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
||
|
new InvalidOperationException(
|
||
|
System.Runtime.Serialization.SR.GetString(System.Runtime.Serialization.SR.XmlIllegalOutsideRoot)));
|
||
|
}
|
||
|
|
||
|
if ((nextNodeType == JsonNodeType.StandaloneText) &&
|
||
|
(nodeType == JsonNodeType.QuotedText))
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
||
|
new XmlException(
|
||
|
SR.GetString(SR.JsonCannotWriteStandaloneTextAfterQuotedText)));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void EnterScope(JsonNodeType currentNodeType)
|
||
|
{
|
||
|
depth++;
|
||
|
if (scopes == null)
|
||
|
{
|
||
|
scopes = new JsonNodeType[4];
|
||
|
}
|
||
|
else if (scopes.Length == depth)
|
||
|
{
|
||
|
JsonNodeType[] newScopes = new JsonNodeType[depth * 2];
|
||
|
Array.Copy(scopes, newScopes, depth);
|
||
|
scopes = newScopes;
|
||
|
}
|
||
|
scopes[depth] = currentNodeType;
|
||
|
}
|
||
|
|
||
|
JsonNodeType ExitScope()
|
||
|
{
|
||
|
JsonNodeType nodeTypeToReturn = scopes[depth];
|
||
|
scopes[depth] = JsonNodeType.None;
|
||
|
depth--;
|
||
|
return nodeTypeToReturn;
|
||
|
}
|
||
|
|
||
|
void InitializeWriter()
|
||
|
{
|
||
|
nodeType = JsonNodeType.None;
|
||
|
dataType = JsonDataType.None;
|
||
|
isWritingDataTypeAttribute = false;
|
||
|
wroteServerTypeAttribute = false;
|
||
|
isWritingServerTypeAttribute = false;
|
||
|
serverTypeValue = null;
|
||
|
attributeText = null;
|
||
|
|
||
|
if (depth != 0)
|
||
|
{
|
||
|
depth = 0;
|
||
|
}
|
||
|
if ((scopes != null) && (scopes.Length > JsonGlobals.maxScopeSize))
|
||
|
{
|
||
|
scopes = null;
|
||
|
}
|
||
|
|
||
|
// Can't let writeState be at Closed if reinitializing.
|
||
|
writeState = WriteState.Start;
|
||
|
endElementBuffer = false;
|
||
|
indentLevel = 0;
|
||
|
}
|
||
|
|
||
|
static bool IsUnicodeNewlineCharacter(char c)
|
||
|
{
|
||
|
// Newline characters in JSON strings need to be encoded on the way out (DevDiv #665974)
|
||
|
// See Unicode 6.2, Table 5-1 (http://www.unicode.org/versions/Unicode6.2.0/ch05.pdf]) for the full list.
|
||
|
|
||
|
// We only care about NEL, LS, and PS, since the other newline characters are all
|
||
|
// control characters so are already encoded.
|
||
|
return (c == '\u0085' || c == '\u2028' || c == '\u2029');
|
||
|
}
|
||
|
|
||
|
void StartText()
|
||
|
{
|
||
|
if (HasOpenAttribute)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.JsonMustUseWriteStringForWritingAttributeValues)));
|
||
|
}
|
||
|
|
||
|
if ((dataType == JsonDataType.None) && (serverTypeValue != null))
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
||
|
new XmlException(SR.GetString(SR.JsonMustSpecifyDataType,
|
||
|
JsonGlobals.typeString, JsonGlobals.objectString, JsonGlobals.serverTypeString)));
|
||
|
}
|
||
|
|
||
|
if (IsWritingNameWithMapping && !WrittenNameWithMapping)
|
||
|
{
|
||
|
// Don't write out any text content unless the local name has been written.
|
||
|
// Not providing a better error message because localization deadline has passed.
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
||
|
new XmlException(SR.GetString(SR.JsonMustSpecifyDataType,
|
||
|
JsonGlobals.itemString, string.Empty, JsonGlobals.itemString)));
|
||
|
}
|
||
|
|
||
|
if ((dataType == JsonDataType.String) ||
|
||
|
(dataType == JsonDataType.None))
|
||
|
{
|
||
|
CheckText(JsonNodeType.QuotedText);
|
||
|
if (nodeType != JsonNodeType.QuotedText)
|
||
|
{
|
||
|
WriteJsonQuote();
|
||
|
}
|
||
|
nodeType = JsonNodeType.QuotedText;
|
||
|
}
|
||
|
else if ((dataType == JsonDataType.Number) ||
|
||
|
(dataType == JsonDataType.Boolean))
|
||
|
{
|
||
|
CheckText(JsonNodeType.StandaloneText);
|
||
|
nodeType = JsonNodeType.StandaloneText;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ThrowInvalidAttributeContent();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void ThrowIfServerTypeWritten(string dataTypeSpecified)
|
||
|
{
|
||
|
if (serverTypeValue != null)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
||
|
new XmlException(SR.GetString(SR.JsonInvalidDataTypeSpecifiedForServerType,
|
||
|
JsonGlobals.typeString, dataTypeSpecified, JsonGlobals.serverTypeString, JsonGlobals.objectString)));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase")] // [....], ToLowerInvariant is just used in Json error message
|
||
|
void ThrowInvalidAttributeContent()
|
||
|
{
|
||
|
if (HasOpenAttribute)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
||
|
new XmlException(SR.GetString(SR.JsonInvalidMethodBetweenStartEndAttribute)));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
||
|
new XmlException(SR.GetString(SR.JsonCannotWriteTextAfterNonTextAttribute,
|
||
|
dataType.ToString().ToLowerInvariant())));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool TrySetWritingNameWithMapping(string localName, string ns)
|
||
|
{
|
||
|
if (localName.Equals(JsonGlobals.itemString) && ns.Equals(JsonGlobals.itemString))
|
||
|
{
|
||
|
nameState = NameState.IsWritingNameWithMapping;
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
void WriteDataTypeServerType()
|
||
|
{
|
||
|
if (dataType != JsonDataType.None)
|
||
|
{
|
||
|
switch (dataType)
|
||
|
{
|
||
|
case JsonDataType.Array:
|
||
|
{
|
||
|
EnterScope(JsonNodeType.Collection);
|
||
|
nodeWriter.WriteText(JsonGlobals.CollectionChar);
|
||
|
indentLevel++;
|
||
|
break;
|
||
|
}
|
||
|
case JsonDataType.Object:
|
||
|
{
|
||
|
EnterScope(JsonNodeType.Object);
|
||
|
nodeWriter.WriteText(JsonGlobals.ObjectChar);
|
||
|
indentLevel++;
|
||
|
break;
|
||
|
}
|
||
|
case JsonDataType.Null:
|
||
|
{
|
||
|
nodeWriter.WriteText(JsonGlobals.nullString);
|
||
|
break;
|
||
|
}
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (serverTypeValue != null)
|
||
|
{
|
||
|
// dataType must be object because we throw in all other case.
|
||
|
WriteServerTypeAttribute();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[SecuritySafeCritical]
|
||
|
unsafe void WriteEscapedJsonString(string str)
|
||
|
{
|
||
|
fixed (char* chars = str)
|
||
|
{
|
||
|
int i = 0;
|
||
|
int j;
|
||
|
for (j = 0; j < str.Length; j++)
|
||
|
{
|
||
|
char ch = chars[j];
|
||
|
if (ch <= FORWARD_SLASH)
|
||
|
{
|
||
|
if (ch == FORWARD_SLASH || ch == JsonGlobals.QuoteChar)
|
||
|
{
|
||
|
nodeWriter.WriteChars(chars + i, j - i);
|
||
|
nodeWriter.WriteText(BACK_SLASH);
|
||
|
nodeWriter.WriteText(ch);
|
||
|
i = j + 1;
|
||
|
}
|
||
|
else if (ch < WHITESPACE)
|
||
|
{
|
||
|
nodeWriter.WriteChars(chars + i, j - i);
|
||
|
nodeWriter.WriteText(BACK_SLASH);
|
||
|
nodeWriter.WriteText('u');
|
||
|
nodeWriter.WriteText(string.Format(CultureInfo.InvariantCulture, "{0:x4}", (int)ch));
|
||
|
i = j + 1;
|
||
|
}
|
||
|
}
|
||
|
else if (ch == BACK_SLASH)
|
||
|
{
|
||
|
nodeWriter.WriteChars(chars + i, j - i);
|
||
|
nodeWriter.WriteText(BACK_SLASH);
|
||
|
nodeWriter.WriteText(ch);
|
||
|
i = j + 1;
|
||
|
}
|
||
|
else if ((ch >= HIGH_SURROGATE_START && (ch <= LOW_SURROGATE_END || ch >= MAX_CHAR)) || IsUnicodeNewlineCharacter(ch))
|
||
|
{
|
||
|
nodeWriter.WriteChars(chars + i, j - i);
|
||
|
nodeWriter.WriteText(BACK_SLASH);
|
||
|
nodeWriter.WriteText('u');
|
||
|
nodeWriter.WriteText(string.Format(CultureInfo.InvariantCulture, "{0:x4}", (int)ch));
|
||
|
i = j + 1;
|
||
|
}
|
||
|
}
|
||
|
if (i < j)
|
||
|
{
|
||
|
nodeWriter.WriteChars(chars + i, j - i);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void WriteIndent()
|
||
|
{
|
||
|
for (int i = 0; i < indentLevel; i++)
|
||
|
{
|
||
|
nodeWriter.WriteText(indentChars);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void WriteNewLine()
|
||
|
{
|
||
|
nodeWriter.WriteText(CARRIAGE_RETURN);
|
||
|
nodeWriter.WriteText(NEWLINE);
|
||
|
}
|
||
|
|
||
|
void WriteJsonElementName(string localName)
|
||
|
{
|
||
|
WriteJsonQuote();
|
||
|
WriteEscapedJsonString(localName);
|
||
|
WriteJsonQuote();
|
||
|
nodeWriter.WriteText(JsonGlobals.NameValueSeparatorChar);
|
||
|
if (indent)
|
||
|
{
|
||
|
nodeWriter.WriteText(WHITESPACE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void WriteJsonQuote()
|
||
|
{
|
||
|
nodeWriter.WriteText(JsonGlobals.QuoteChar);
|
||
|
}
|
||
|
|
||
|
void WritePrimitiveValue(object value)
|
||
|
{
|
||
|
if (IsClosed)
|
||
|
{
|
||
|
ThrowClosed();
|
||
|
}
|
||
|
|
||
|
if (value == null)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("value"));
|
||
|
}
|
||
|
|
||
|
if (value is ulong)
|
||
|
{
|
||
|
WriteValue((ulong)value);
|
||
|
}
|
||
|
else if (value is string)
|
||
|
{
|
||
|
WriteValue((string)value);
|
||
|
}
|
||
|
else if (value is int)
|
||
|
{
|
||
|
WriteValue((int)value);
|
||
|
}
|
||
|
else if (value is long)
|
||
|
{
|
||
|
WriteValue((long)value);
|
||
|
}
|
||
|
else if (value is bool)
|
||
|
{
|
||
|
WriteValue((bool)value);
|
||
|
}
|
||
|
else if (value is double)
|
||
|
{
|
||
|
WriteValue((double)value);
|
||
|
}
|
||
|
else if (value is DateTime)
|
||
|
{
|
||
|
WriteValue((DateTime)value);
|
||
|
}
|
||
|
else if (value is float)
|
||
|
{
|
||
|
WriteValue((float)value);
|
||
|
}
|
||
|
else if (value is decimal)
|
||
|
{
|
||
|
WriteValue((decimal)value);
|
||
|
}
|
||
|
else if (value is XmlDictionaryString)
|
||
|
{
|
||
|
WriteValue((XmlDictionaryString)value);
|
||
|
}
|
||
|
else if (value is UniqueId)
|
||
|
{
|
||
|
WriteValue((UniqueId)value);
|
||
|
}
|
||
|
else if (value is Guid)
|
||
|
{
|
||
|
WriteValue((Guid)value);
|
||
|
}
|
||
|
else if (value is TimeSpan)
|
||
|
{
|
||
|
WriteValue((TimeSpan)value);
|
||
|
}
|
||
|
else if (value.GetType().IsArray)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.JsonNestedArraysNotSupported), "value"));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
base.WriteValue(value);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void WriteServerTypeAttribute()
|
||
|
{
|
||
|
string value = serverTypeValue;
|
||
|
JsonDataType oldDataType = dataType;
|
||
|
NameState oldNameState = nameState;
|
||
|
WriteStartElement(JsonGlobals.serverTypeString);
|
||
|
WriteValue(value);
|
||
|
WriteEndElement();
|
||
|
dataType = oldDataType;
|
||
|
nameState = oldNameState;
|
||
|
wroteServerTypeAttribute = true;
|
||
|
}
|
||
|
|
||
|
void WriteValue(ulong value)
|
||
|
{
|
||
|
StartText();
|
||
|
nodeWriter.WriteUInt64Text(value);
|
||
|
}
|
||
|
|
||
|
void WriteValue(Array array)
|
||
|
{
|
||
|
// This method is called only if WriteValue(object) is called with an array
|
||
|
// The contract for XmlWriter.WriteValue(object) requires that this object array be written out as a string.
|
||
|
// E.g. WriteValue(new int[] { 1, 2, 3}) should be equivalent to WriteString("1 2 3").
|
||
|
JsonDataType oldDataType = dataType;
|
||
|
// Set attribute mode to String because WritePrimitiveValue might write numerical text.
|
||
|
// Calls to methods that write numbers can't be mixed with calls that write quoted text unless the attribute mode is explictly string.
|
||
|
dataType = JsonDataType.String;
|
||
|
StartText();
|
||
|
for (int i = 0; i < array.Length; i++)
|
||
|
{
|
||
|
if (i != 0)
|
||
|
{
|
||
|
nodeWriter.WriteText(JsonGlobals.WhitespaceChar);
|
||
|
}
|
||
|
WritePrimitiveValue(array.GetValue(i));
|
||
|
}
|
||
|
dataType = oldDataType;
|
||
|
}
|
||
|
|
||
|
class JsonNodeWriter : XmlUTF8NodeWriter
|
||
|
{
|
||
|
[SecurityCritical]
|
||
|
internal unsafe void WriteChars(char* chars, int charCount)
|
||
|
{
|
||
|
base.UnsafeWriteUTF8Chars(chars, charCount);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|