Esempio n. 1
0
        public static CBORObject ParseJSONNumber(
            string str,
            bool integersOnly,
            bool positiveOnly,
            bool preserveNegativeZero)
        {
            if (String.IsNullOrEmpty(str))
            {
                return(null);
            }
            if (integersOnly)
            {
                for (var i = 0; i < str.Length; ++i)
                {
                    if (str[i] >= '0' && str[i] <= '9' && (i > 0 || str[i] != '-'))
                    {
                        return(null);
                    }
                }
            }
            JSONOptions jo = preserveNegativeZero ? PreserveNegZeroYes :
                             PreserveNegZeroNo;

            return((positiveOnly && str[0] == '-') ? null :
                   ParseJSONNumber(str,
                                   0,
                                   str.Length,
                                   jo));
        }
Esempio n. 2
0
        internal static CBORObject ParseSmallNumber(int digit, JSONOptions
                                                    options)
        {
      #if DEBUG
            if (digit < 0)
            {
                throw new ArgumentException("digit (" + digit + ") is not greater" +
                                            "\u0020or equal to 0");
            }
      #endif

            if (options != null && options.NumberConversion ==
                JSONOptions.ConversionMode.Double)
            {
                return(CBORObject.FromObject((double)digit));
            }
            else if (options != null && options.NumberConversion ==
                     JSONOptions.ConversionMode.Decimal128)
            {
                return(CBORObject.FromObject(EDecimal.FromInt32(digit)));
            }
            else
            {
                // NOTE: Assumes digit is nonnegative, so PreserveNegativeZeros is irrelevant
                return(CBORObject.FromObject(digit));
            }
        }
Esempio n. 3
0
 /// <summary>Parses a number whose format follows the JSON
 /// specification (RFC 8259) and converts that number to a CBOR
 /// object.</summary>
 /// <param name='str'>A text string to parse as a JSON number.</param>
 /// <param name='offset'>An index, starting at 0, showing where the
 /// desired portion of <paramref name='str'/> begins.</param>
 /// <param name='count'>The length, in code units, of the desired
 /// portion of <paramref name='str'/> (but not more than <paramref
 /// name='str'/> 's length).</param>
 /// <param name='options'>An object containing options to control how
 /// JSON numbers are decoded to CBOR objects. Can be null, in which
 /// case a JSONOptions object with all default properties is used
 /// instead.</param>
 /// <returns>A CBOR object that represents the parsed number. Returns
 /// null if the parsing fails, including if the string is null or empty
 /// or <paramref name='count'/> is 0 or less.</returns>
 /// <exception cref='ArgumentNullException'>The parameter <paramref
 /// name='str'/> is null.</exception>
 /// <exception cref='ArgumentException'>Unsupported conversion
 /// kind.</exception>
 /// <remarks>Roughly speaking, a valid JSON number consists of an
 /// optional minus sign, one or more basic digits (starting with 1 to 9
 /// unless there is only one digit and that digit is 0), an optional
 /// decimal point (".", full stop) with one or more basic digits, and
 /// an optional letter E or e with an optional plus or minus sign and
 /// one or more basic digits (the exponent). A string representing a
 /// valid JSON number is not allowed to contain white space characters,
 /// including spaces.</remarks>
 public static CBORObject ParseJSONNumber(
     string str,
     int offset,
     int count,
     JSONOptions options)
 {
     return(ParseJSONNumber(str, offset, count, options, null));
 }
Esempio n. 4
0
 public CBORJson(CharacterInputWithCount reader, JSONOptions options)
 {
     this.reader              = reader;
     this.sb                  = null;
     this.options             = options;
     this.jsonSequenceMode    = false;
     this.recordSeparatorSeen = false;
 }
Esempio n. 5
0
        internal static CBORObject ParseJSONValue(
            CharacterInputWithCount reader,
            JSONOptions options,
            int[] nextChar)
        {
            var cj = new CBORJson(reader, options);

            return(cj.ParseJSON(nextChar));
        }
Esempio n. 6
0
 /// <summary>Parses a number whose format follows the JSON
 /// specification (RFC 8259) and converts that number to a CBOR
 /// object.</summary>
 /// <param name='str'>A text string to parse as a JSON number.</param>
 /// <param name='options'>An object containing options to control how
 /// JSON numbers are decoded to CBOR objects. Can be null, in which
 /// case a JSONOptions object with all default properties is used
 /// instead.</param>
 /// <returns>A CBOR object that represents the parsed number. Returns
 /// null if the parsing fails, including if the string is null or
 /// empty.</returns>
 /// <remarks>Roughly speaking, a valid JSON number consists of an
 /// optional minus sign, one or more basic digits (starting with 1 to 9
 /// unless there is only one digit and that digit is 0), an optional
 /// decimal point (".", full stop) with one or more basic digits, and
 /// an optional letter E or e with an optional plus or minus sign and
 /// one or more basic digits (the exponent). A string representing a
 /// valid JSON number is not allowed to contain white space characters,
 /// including spaces.</remarks>
 public static CBORObject ParseJSONNumber(
     string str,
     JSONOptions options)
 {
     return(String.IsNullOrEmpty(str) ? null :
            ParseJSONNumber(str,
                            0,
                            str.Length,
                            options));
 }
Esempio n. 7
0
 internal static void WriteJSONToInternal(
     CBORObject obj,
     StringOutput writer,
     JSONOptions options)
 {
     if (obj.Type == CBORType.Array || obj.Type == CBORType.Map)
     {
         var stack = new List <CBORObject>();
         WriteJSONToInternal(obj, writer, options, stack);
     }
     else
     {
         WriteJSONToInternal(obj, writer, options, null);
     }
 }
Esempio n. 8
0
        internal static CBORObject ParseJSONValue(
            byte[] bytes,
            int index,
            int endPos,
            JSONOptions options)
        {
            var        nextchar = new int[1];
            var        cj       = new CBORJson2(bytes, index, endPos, options);
            CBORObject obj      = cj.ParseJSON(nextchar);

            if (nextchar[0] != -1)
            {
                cj.RaiseError("End of bytes not reached");
            }
            return(obj);
        }
Esempio n. 9
0
        internal static CBORObject ParseJSONValue(
            byte[] bytes,
            int index,
            int endPos,
            JSONOptions options,
            int[] nextchar)
        {
      #if DEBUG
            if (bytes == null)
            {
                throw new ArgumentNullException(nameof(bytes));
            }
            if (index < 0)
            {
                throw new ArgumentException("index (" + index + ") is not greater or" +
                                            "\u0020equal to 0");
            }
            if (index > bytes.Length)
            {
                throw new ArgumentException("index (" + index + ") is not less or" +
                                            "\u0020equal to " + bytes.Length);
            }
            if (endPos < 0)
            {
                throw new ArgumentException("endPos (" + endPos + ") is not greater" +
                                            "\u0020or equal to 0");
            }
            if (endPos > bytes.Length)
            {
                throw new ArgumentException("endPos (" + endPos + ") is not less or" +
                                            "\u0020equal to " + bytes.Length);
            }
            if (endPos < index)
            {
                throw new ArgumentException("endPos (" + endPos + ") is not greater" +
                                            "\u0020or equal to " + index);
            }
      #endif

            var cj = new CBORJson2(bytes, index, endPos, options);
            return(cj.ParseJSON(nextchar));
        }
Esempio n. 10
0
        public CBORJson2(byte[] bytes, int index, int endPos, JSONOptions
                         options)
        {
      #if DEBUG
            if (bytes == null)
            {
                throw new ArgumentNullException(nameof(bytes));
            }
            if (index < 0)
            {
                throw new ArgumentException("index (" + index + ") is not greater or" +
                                            "\u0020equal to 0");
            }
            if (index > bytes.Length)
            {
                throw new ArgumentException("index (" + index + ") is not less or" +
                                            "\u0020equal to " + bytes.Length);
            }
            if (endPos < 0)
            {
                throw new ArgumentException("endPos (" + endPos + ") is not greater" +
                                            "\u0020or equal to 0");
            }
            if (endPos > bytes.Length)
            {
                throw new ArgumentException("endPos (" + endPos + ") is not less or" +
                                            "\u0020equal to " + bytes.Length);
            }
            if (endPos < index)
            {
                throw new ArgumentException("endPos (" + endPos + ") is not greater" +
                                            "\u0020or equal to " + index);
            }
      #endif

            this.sb      = null;
            this.bytes   = bytes;
            this.index   = index;
            this.endPos  = endPos;
            this.options = options;
        }
Esempio n. 11
0
   public CBORJson3(string jstring, int index, int endPos, JSONOptions
                    options)
   {
 #if DEBUG
       if (jstring == null)
       {
           throw new ArgumentNullException(nameof(jstring));
       }
       if (index < 0)
       {
           throw new ArgumentException("index (" + index + ") is not greater or" +
                                       "\u0020equal to 0");
       }
       if (index > jstring.Length)
       {
           throw new ArgumentException("index (" + index + ") is not less or" +
                                       "\u0020equal to " + jstring.Length);
       }
       if (endPos < 0)
       {
           throw new ArgumentException("endPos (" + endPos + ") is not greater" +
                                       "\u0020or equal to 0");
       }
       if (endPos > jstring.Length)
       {
           throw new ArgumentException("endPos (" + endPos + ") is not less or" +
                                       "\u0020equal to " + jstring.Length);
       }
       if (endPos < index)
       {
           throw new ArgumentException("endPos (" + endPos + ") is not greater" +
                                       "\u0020or equal to " + index);
       }
 #endif
       this.sb      = null;
       this.jstring = jstring;
       this.index   = index;
       this.endPos  = endPos;
       this.options = options;
   }
Esempio n. 12
0
        internal static CBORObject ParseJSONNumber(
            string str,
            int offset,
            int count,
            JSONOptions options,
            int[] endOfNumber)
        {
            if (String.IsNullOrEmpty(str) || count <= 0)
            {
                return(null);
            }
            if (offset < 0 || offset > str.Length)
            {
                return(null);
            }
            if (count > str.Length || str.Length - offset < count)
            {
                return(null);
            }
            JSONOptions opt = options ?? DefaultOptions;
            bool        preserveNegativeZero = options.PreserveNegativeZero;

            JSONOptions.ConversionMode kind = options.NumberConversion;
            int endPos        = offset + count;
            int initialOffset = offset;
            var negative      = false;

            if (str[initialOffset] == '-')
            {
                ++offset;
                negative = true;
            }
            int numOffset              = offset;
            var haveDecimalPoint       = false;
            var haveDigits             = false;
            var haveDigitsAfterDecimal = false;
            var haveExponent           = false;
            int i = offset;
            var decimalPointPos = -1;
            // Check syntax
            int k = i;

            if (endPos - 1 > k && str[k] == '0' && str[k + 1] >= '0' &&
                str[k + 1] <= '9')
            {
                if (endOfNumber != null)
                {
                    endOfNumber[0] = k + 2;
                }
                return(null);
            }
            for (; k < endPos; ++k)
            {
                char c = str[k];
                if (c >= '0' && c <= '9')
                {
                    haveDigits              = true;
                    haveDigitsAfterDecimal |= haveDecimalPoint;
                }
                else if (c == '.')
                {
                    if (!haveDigits || haveDecimalPoint)
                    {
                        // no digits before the decimal point,
                        // or decimal point already seen
                        if (endOfNumber != null)
                        {
                            endOfNumber[0] = k;
                        }
                        return(null);
                    }
                    haveDecimalPoint = true;
                    decimalPointPos  = k;
                }
                else if (c == 'E' || c == 'e')
                {
                    ++k;
                    haveExponent = true;
                    break;
                }
                else
                {
                    if (endOfNumber != null)
                    {
                        endOfNumber[0] = k;
                        // Check if character can validly appear after a JSON number
                        if (c != ',' && c != ']' && c != '}' &&
                            c != 0x20 && c != 0x0a && c != 0x0d && c != 0x09)
                        {
                            return(null);
                        }
                        else
                        {
                            endPos = k;
                            break;
                        }
                    }
                    return(null);
                }
            }
            if (!haveDigits || (haveDecimalPoint && !haveDigitsAfterDecimal))
            {
                if (endOfNumber != null)
                {
                    endOfNumber[0] = k;
                }
                return(null);
            }
            var exponentPos = -1;
            var negativeExp = false;

            if (haveExponent)
            {
                haveDigits = false;
                if (k == endPos)
                {
                    if (endOfNumber != null)
                    {
                        endOfNumber[0] = k;
                    }
                    return(null);
                }
                char c = str[k];
                if (c == '+')
                {
                    ++k;
                }
                else if (c == '-')
                {
                    negativeExp = true;
                    ++k;
                }
                for (; k < endPos; ++k)
                {
                    c = str[k];
                    if (c >= '0' && c <= '9')
                    {
                        if (exponentPos < 0 && c != '0')
                        {
                            exponentPos = k;
                        }
                        haveDigits = true;
                    }
                    else if (endOfNumber != null)
                    {
                        endOfNumber[0] = k;
                        // Check if character can validly appear after a JSON number
                        if (c != ',' && c != ']' && c != '}' &&
                            c != 0x20 && c != 0x0a && c != 0x0d && c != 0x09)
                        {
                            return(null);
                        }
                        else
                        {
                            endPos = k;
                            break;
                        }
                    }
                    else
                    {
                        return(null);
                    }
                }
                if (!haveDigits)
                {
                    if (endOfNumber != null)
                    {
                        endOfNumber[0] = k;
                    }
                    return(null);
                }
            }
            if (endOfNumber != null)
            {
                endOfNumber[0] = endPos;
            }
            if (exponentPos >= 0 && endPos - exponentPos > 20)
            {
                // Exponent too high for precision to overcome (which
                // has a length no bigger than Int32.MaxValue, which is 10 digits
                // long)
                if (negativeExp)
                {
                    // underflow
                    if (kind == JSONOptions.ConversionMode.Double ||
                        kind == JSONOptions.ConversionMode.IntOrFloat)
                    {
                        if (!negative)
                        {
                            return(CBORObject.FromObject((double)0.0));
                        }
                        else
                        {
                            return(CBORObject.FromFloatingPointBits(0x8000, 2));
                        }
                    }
                    else if (kind ==
                             JSONOptions.ConversionMode.IntOrFloatFromDouble)
                    {
                        return(CBORObject.FromObject(0));
                    }
                }
                else
                {
                    // overflow
                    if (kind == JSONOptions.ConversionMode.Double ||
                        kind == JSONOptions.ConversionMode.IntOrFloatFromDouble ||
                        kind == JSONOptions.ConversionMode.IntOrFloat)
                    {
                        return(CBORObject.FromObject(
                                   negative ? Double.NegativeInfinity : Double.PositiveInfinity));
                    }
                    else if (kind == JSONOptions.ConversionMode.Decimal128)
                    {
                        return(CBORObject.FromObject(negative ?
                                                     EDecimal.NegativeInfinity : EDecimal.PositiveInfinity));
                    }
                }
            }
            if (!haveExponent && !haveDecimalPoint &&
                (endPos - numOffset) <= 16)
            {
                // Very common case of all-digit JSON number strings
                // less than 2^53 (with or without number sign)
                long v  = 0L;
                int  vi = numOffset;
                for (; vi < endPos; ++vi)
                {
                    v = (v * 10) + (int)(str[vi] - '0');
                }
                if ((v != 0 || !negative) && v < (1L << 53) - 1)
                {
                    if (negative)
                    {
                        v = -v;
                    }
                    if (kind == JSONOptions.ConversionMode.Double)
                    {
                        return(CBORObject.FromObject((double)v));
                    }
                    else if (kind == JSONOptions.ConversionMode.Decimal128)
                    {
                        return(CBORObject.FromObject(EDecimal.FromInt64(v)));
                    }
                    else
                    {
                        return(CBORObject.FromObject(v));
                    }
                }
            }
            if (kind == JSONOptions.ConversionMode.Full)
            {
                if (!haveDecimalPoint && !haveExponent)
                {
                    EInteger ei = EInteger.FromSubstring(str, initialOffset, endPos);
                    if (preserveNegativeZero && ei.IsZero && negative)
                    {
                        // TODO: In next major version, change to EDecimal.NegativeZero
                        return(CBORObject.FromFloatingPointBits(0x8000, 2));
                    }
                    return(CBORObject.FromObject(ei));
                }
                if (!haveExponent && haveDecimalPoint && (endPos - numOffset) <= 19)
                {
                    // No more than 18 digits plus one decimal point (which
                    // should fit a long)
                    long lv   = 0L;
                    int  expo = -(endPos - (decimalPointPos + 1));
                    int  vi   = numOffset;
                    for (; vi < decimalPointPos; ++vi)
                    {
                        lv = checked ((lv * 10) + (int)(str[vi] - '0'));
                    }
                    for (vi = decimalPointPos + 1; vi < endPos; ++vi)
                    {
                        lv = checked ((lv * 10) + (int)(str[vi] - '0'));
                    }
                    if (negative)
                    {
                        lv = -lv;
                    }
                    if (!negative || lv != 0)
                    {
                        if (expo == 0)
                        {
                            return(CBORObject.FromObject(lv));
                        }
                        else
                        {
                            CBORObject cbor = CBORObject.FromObject(
                                new CBORObject[] {
                                CBORObject.FromObject(expo),
                                CBORObject.FromObject(lv),
                            });
                            return(cbor.WithTag(4));
                        }
                    }
                }
                EDecimal ed = EDecimal.FromString(
                    str,
                    initialOffset,
                    endPos - initialOffset);
                if (ed.IsZero && negative)
                {
                    if (ed.Exponent.IsZero)
                    {
                        // TODO: In next major version, use EDecimal
                        // for preserveNegativeZero
                        return(preserveNegativeZero ?
                               CBORObject.FromFloatingPointBits(0x8000, 2) :
                               CBORObject.FromObject(0));
                    }
                    else if (!preserveNegativeZero)
                    {
                        return(CBORObject.FromObject(ed.Negate()));
                    }
                    else
                    {
                        return(CBORObject.FromObject(ed));
                    }
                }
                else
                {
                    return(ed.Exponent.IsZero ? CBORObject.FromObject(ed.Mantissa) :
                           CBORObject.FromObject(ed));
                }
            }
            else if (kind == JSONOptions.ConversionMode.Double)
            {
                // TODO: Avoid converting to double
                double dbl = EFloat.FromString(
                    str,
                    initialOffset,
                    endPos - initialOffset,
                    EContext.Binary64).ToDouble();
                if (!preserveNegativeZero && dbl == 0.0)
                {
                    dbl = 0.0;
                }
                return(CBORObject.FromObject(dbl));
            }
            else if (kind == JSONOptions.ConversionMode.Decimal128)
            {
                EDecimal ed = EDecimal.FromString(
                    str,
                    initialOffset,
                    endPos - initialOffset,
                    EContext.Decimal128);
                if (!preserveNegativeZero && ed.IsNegative && ed.IsZero)
                {
                    ed = ed.Negate();
                }
                return(CBORObject.FromObject(ed));
            }
            else if (kind == JSONOptions.ConversionMode.IntOrFloatFromDouble)
            {
                // TODO: Avoid converting to double
                double dbl = EFloat.FromString(
                    str,
                    initialOffset,
                    endPos - initialOffset,
                    EContext.Binary64).ToDouble();
                if (!Double.IsNaN(dbl) && dbl >= -9007199254740991.0 &&
                    dbl <= 9007199254740991.0 && Math.Floor(dbl) == dbl)
                {
                    var idbl = (long)dbl;
                    return(CBORObject.FromObject(idbl));
                }
                return(CBORObject.FromObject(dbl));
            }
            else if (kind == JSONOptions.ConversionMode.IntOrFloat)
            {
                EContext ctx = EContext.Binary64.WithBlankFlags();
                // TODO: Avoid converting to double
                double dbl = EFloat.FromString(
                    str,
                    initialOffset,
                    endPos - initialOffset,
                    ctx).ToDouble();
                if ((ctx.Flags & EContext.FlagInexact) != 0)
                {
                    // Inexact conversion to double, meaning that the string doesn't
                    // represent an integer in [-(2^53)+1, 2^53), which is representable
                    // exactly as double, so treat as ConversionMode.Double
                    if (!preserveNegativeZero && dbl == 0.0)
                    {
                        dbl = 0.0;
                    }
                    return(CBORObject.FromObject(dbl));
                }
                else
                {
                    // Exact conversion; treat as ConversionMode.IntToFloatFromDouble
                    if (!Double.IsNaN(dbl) && dbl >= -9007199254740991.0 &&
                        dbl <= 9007199254740991.0 && Math.Floor(dbl) == dbl)
                    {
                        var idbl = (long)dbl;
                        return(CBORObject.FromObject(idbl));
                    }
                    return(CBORObject.FromObject(dbl));
                }
            }
            else
            {
                throw new ArgumentException("Unsupported conversion kind.");
            }
        }
Esempio n. 13
0
        internal static CBORObject[] ParseJSONSequence(
            CharacterInputWithCount reader,
            JSONOptions options,
            int[] nextChar)
        {
            var cj = new CBORJson(reader, options);

            cj.SetJSONSequenceMode();
            bool seenSeparator = cj.SkipRecordSeparators(nextChar, false);

            if (nextChar[0] >= 0 && !seenSeparator)
            {
                // Stream is not empty and did not begin with
                // record separator
                cj.RaiseError("Not a JSON text sequence");
            }
            else if (nextChar[0] < 0 && !seenSeparator)
            {
                // Stream is empty
                return(new CBORObject[0]);
            }
            else if (nextChar[0] < 0)
            {
                // Stream had only record separators, so we found
                // a truncated JSON text
                return(new CBORObject[] { null });
            }
            var list = new List <CBORObject>();

            while (true)
            {
                CBORObject co;
                try {
                    co = cj.ParseJSON(nextChar);
                } catch (CBORException) {
                    cj.SkipToEnd();
                    co = null;
                }
                if (co != null && nextChar[0] >= 0)
                {
                    // End of JSON text not reached
                    cj.SkipToEnd();
                    co = null;
                }
                list.Add(co);
                if (!cj.recordSeparatorSeen)
                {
                    // End of the stream was reached
                    nextChar[0] = -1;
                    break;
                }
                else
                {
                    // A record separator was seen, so
                    // another JSON text follows
                    cj.ResetJSONSequenceMode();
                    cj.SkipRecordSeparators(nextChar, true);
                    if (nextChar[0] < 0)
                    {
                        // Rest of stream had only record separators, so we found
                        // a truncated JSON text
                        list.Add(null);
                        break;
                    }
                }
            }
            return((CBORObject[])list.ToArray());
        }
Esempio n. 14
0
        internal static void WriteJSONToInternal(
            CBORObject obj,
            StringOutput writer,
            JSONOptions options,
            IList <CBORObject> stack)
        {
            if (obj.IsNumber)
            {
                writer.WriteString(CBORNumber.FromCBORObject(obj).ToJSONString());
                return;
            }
            switch (obj.Type)
            {
            case CBORType.Integer:
            case CBORType.FloatingPoint: {
                CBORObject untaggedObj = obj.Untag();
                writer.WriteString(
                    CBORNumber.FromCBORObject(untaggedObj).ToJSONString());
                break;
            }

            case CBORType.Boolean: {
                if (obj.IsTrue)
                {
                    writer.WriteString("true");
                    return;
                }
                if (obj.IsFalse)
                {
                    writer.WriteString("false");
                    return;
                }
                return;
            }

            case CBORType.SimpleValue: {
                writer.WriteString("null");
                return;
            }

            case CBORType.ByteString: {
                byte[] byteArray = obj.GetByteString();
                if (byteArray.Length == 0)
                {
                    writer.WriteString("\"\"");
                    return;
                }
                writer.WriteCodePoint((int)'\"');
                if (obj.HasTag(22))
                {
                    // Base64 with padding
                    Base64.WriteBase64(
                        writer,
                        byteArray,
                        0,
                        byteArray.Length,
                        true);
                }
                else if (obj.HasTag(23))
                {
                    // Write as base16
                    for (int i = 0; i < byteArray.Length; ++i)
                    {
                        writer.WriteCodePoint((int)Hex16[(byteArray[i] >> 4) & 15]);
                        writer.WriteCodePoint((int)Hex16[byteArray[i] & 15]);
                    }
                }
                else
                {
                    // Base64url no padding
                    Base64.WriteBase64URL(
                        writer,
                        byteArray,
                        0,
                        byteArray.Length,
                        false);
                }
                writer.WriteCodePoint((int)'\"');
                break;
            }

            case CBORType.TextString: {
                string thisString = obj.AsString();
                if (thisString.Length == 0)
                {
                    writer.WriteString("\"\"");
                    return;
                }
                writer.WriteCodePoint((int)'\"');
                WriteJSONStringUnquoted(thisString, writer, options);
                writer.WriteCodePoint((int)'\"');
                break;
            }

            case CBORType.Array: {
                writer.WriteCodePoint((int)'[');
                for (var i = 0; i < obj.Count; ++i)
                {
                    if (i > 0)
                    {
                        writer.WriteCodePoint((int)',');
                    }
                    bool pop = CheckCircularRef(stack, obj, obj[i]);
                    WriteJSONToInternal(obj[i], writer, options, stack);
                    PopRefIfNeeded(stack, pop);
                }
                writer.WriteCodePoint((int)']');
                break;
            }

            case CBORType.Map: {
                var first            = true;
                var hasNonStringKeys = false;
                ICollection <KeyValuePair <CBORObject, CBORObject> > entries =
                    obj.Entries;
                foreach (KeyValuePair <CBORObject, CBORObject> entry in entries)
                {
                    CBORObject key = entry.Key;
                    if (key.Type != CBORType.TextString ||
                        key.IsTagged)
                    {
                        // treat a non-text-string item or a tagged item
                        // as having non-string keys
                        hasNonStringKeys = true;
                        break;
                    }
                }
                if (!hasNonStringKeys)
                {
                    writer.WriteCodePoint((int)'{');
                    foreach (KeyValuePair <CBORObject, CBORObject> entry in entries)
                    {
                        CBORObject key   = entry.Key;
                        CBORObject value = entry.Value;
                        if (!first)
                        {
                            writer.WriteCodePoint((int)',');
                        }
                        writer.WriteCodePoint((int)'\"');
                        WriteJSONStringUnquoted(key.AsString(), writer, options);
                        writer.WriteCodePoint((int)'\"');
                        writer.WriteCodePoint((int)':');
                        bool pop = CheckCircularRef(stack, obj, value);
                        WriteJSONToInternal(value, writer, options, stack);
                        PopRefIfNeeded(stack, pop);
                        first = false;
                    }
                    writer.WriteCodePoint((int)'}');
                }
                else
                {
                    // This map has non-string keys
                    IDictionary <string, CBORObject> stringMap = new
                                                                 Dictionary <string, CBORObject>();
                    // Copy to a map with String keys, since
                    // some keys could be duplicates
                    // when serialized to strings
                    foreach (KeyValuePair <CBORObject, CBORObject> entry
                             in entries)
                    {
                        CBORObject key   = entry.Key;
                        CBORObject value = entry.Value;
                        string     str   = null;
                        switch (key.Type)
                        {
                        case CBORType.TextString:
                            str = key.AsString();
                            break;

                        case CBORType.Array:
                        case CBORType.Map: {
                            var  sb  = new StringBuilder();
                            var  sw  = new StringOutput(sb);
                            bool pop = CheckCircularRef(stack, obj, key);
                            WriteJSONToInternal(key, sw, options, stack);
                            PopRefIfNeeded(stack, pop);
                            str = sb.ToString();
                            break;
                        }

                        default: str = key.ToJSONString(options);
                            break;
                        }
                        if (stringMap.ContainsKey(str))
                        {
                            throw new CBORException(
                                      "Duplicate JSON string equivalents of map" +
                                      "\u0020keys");
                        }
                        stringMap[str] = value;
                    }
                    first = true;
                    writer.WriteCodePoint((int)'{');
                    foreach (KeyValuePair <string, CBORObject> entry in stringMap)
                    {
                        string     key   = entry.Key;
                        CBORObject value = entry.Value;
                        if (!first)
                        {
                            writer.WriteCodePoint((int)',');
                        }
                        writer.WriteCodePoint((int)'\"');
                        WriteJSONStringUnquoted((string)key, writer, options);
                        writer.WriteCodePoint((int)'\"');
                        writer.WriteCodePoint((int)':');
                        bool pop = CheckCircularRef(stack, obj, value);
                        WriteJSONToInternal(value, writer, options, stack);
                        PopRefIfNeeded(stack, pop);
                        first = false;
                    }
                    writer.WriteCodePoint((int)'}');
                }
                break;
            }

            default:
                throw new InvalidOperationException("Unexpected item" +
                                                    "\u0020type");
            }
        }
Esempio n. 15
0
        internal static void WriteJSONStringUnquoted(
            string str,
            StringOutput sb,
            JSONOptions options)
        {
            var first = true;

            for (var i = 0; i < str.Length; ++i)
            {
                char c = str[i];
                if (c == '\\' || c == '"')
                {
                    if (first)
                    {
                        first = false;
                        sb.WriteString(str, 0, i);
                    }
                    sb.WriteCodePoint((int)'\\');
                    sb.WriteCodePoint((int)c);
                }
                else if (c < 0x20 || (c >= 0x7f && (c == 0x2028 || c == 0x2029 ||
                                                    (c >= 0x7f && c <= 0xa0) || c == 0xfeff || c == 0xfffe ||
                                                    c == 0xffff)))
                {
                    // Control characters, and also the line and paragraph separators
                    // which apparently can't appear in JavaScript (as opposed to
                    // JSON) strings
                    if (first)
                    {
                        first = false;
                        sb.WriteString(str, 0, i);
                    }
                    if (c == 0x0d)
                    {
                        sb.WriteString("\\r");
                    }
                    else if (c == 0x0a)
                    {
                        sb.WriteString("\\n");
                    }
                    else if (c == 0x08)
                    {
                        sb.WriteString("\\b");
                    }
                    else if (c == 0x0c)
                    {
                        sb.WriteString("\\f");
                    }
                    else if (c == 0x09)
                    {
                        sb.WriteString("\\t");
                    }
                    else if (c == 0x85)
                    {
                        sb.WriteString("\\u0085");
                    }
                    else if (c >= 0x100)
                    {
                        sb.WriteString("\\u");
                        sb.WriteCodePoint((int)Hex16[(int)((c >> 12) & 15)]);
                        sb.WriteCodePoint((int)Hex16[(int)((c >> 8) & 15)]);
                        sb.WriteCodePoint((int)Hex16[(int)((c >> 4) & 15)]);
                        sb.WriteCodePoint((int)Hex16[(int)(c & 15)]);
                    }
                    else
                    {
                        sb.WriteString("\\u00");
                        sb.WriteCodePoint((int)Hex16[(int)(c >> 4)]);
                        sb.WriteCodePoint((int)Hex16[(int)(c & 15)]);
                    }
                }
                else
                {
                    if ((c & 0xfc00) == 0xd800)
                    {
                        if (i >= str.Length - 1 || (str[i + 1] & 0xfc00) != 0xdc00)
                        {
                            // NOTE: RFC 8259 doesn't prohibit any particular
                            // error-handling behavior when a writer of JSON
                            // receives a string with an unpaired surrogate.
                            if (options.ReplaceSurrogates)
                            {
                                if (first)
                                {
                                    first = false;
                                    sb.WriteString(str, 0, i);
                                }
                                // Replace unpaired surrogate with U+FFFD
                                c = (char)0xfffd;
                            }
                            else
                            {
                                throw new CBORException("Unpaired surrogate in string");
                            }
                        }
                    }
                    if (!first)
                    {
                        if ((c & 0xfc00) == 0xd800)
                        {
                            sb.WriteString(str, i, 2);
                            ++i;
                        }
                        else
                        {
                            sb.WriteCodePoint((int)c);
                        }
                    }
                }
            }
            if (first)
            {
                sb.WriteString(str);
            }
        }