internal static void WriteJSONToInternal( CBORObject obj, StringOutput writer) { int type = obj.ItemType; object thisItem = obj.ThisItem; switch (type) { case CBORObject.CBORObjectTypeSimpleValue: { if (obj.IsTrue) { writer.WriteString("true"); return; } if (obj.IsFalse) { writer.WriteString("false"); return; } writer.WriteString("null"); return; } case CBORObject.CBORObjectTypeSingle: { var f = (float)thisItem; if (Single.IsNegativeInfinity(f) || Single.IsPositiveInfinity(f) || Single.IsNaN(f)) { writer.WriteString("null"); return; } writer.WriteString( CBORObject.TrimDotZero( CBORUtilities.SingleToString(f))); return; } case CBORObject.CBORObjectTypeDouble: { var f = (double)thisItem; if (Double.IsNegativeInfinity(f) || Double.IsPositiveInfinity(f) || Double.IsNaN(f)) { writer.WriteString("null"); return; } string dblString = CBORUtilities.DoubleToString(f); writer.WriteString( CBORObject.TrimDotZero(dblString)); return; } case CBORObject.CBORObjectTypeInteger: { var longItem = (long)thisItem; writer.WriteString(CBORUtilities.LongToString(longItem)); return; } case CBORObject.CBORObjectTypeBigInteger: { writer.WriteString( CBORUtilities.BigIntToString((EInteger)thisItem)); return; } case CBORObject.CBORObjectTypeExtendedDecimal: { var dec = (EDecimal)thisItem; if (dec.IsInfinity() || dec.IsNaN()) { writer.WriteString("null"); } else { writer.WriteString(dec.ToString()); } return; } case CBORObject.CBORObjectTypeExtendedFloat: { var flo = (EFloat)thisItem; if (flo.IsInfinity() || flo.IsNaN()) { writer.WriteString("null"); return; } if (flo.IsFinite && flo.Exponent.Abs().CompareTo((EInteger)2500) > 0) { // Too inefficient to convert to a decimal number // from a bigfloat with a very high exponent, // so convert to double instead double f = flo.ToDouble(); if (Double.IsNegativeInfinity(f) || Double.IsPositiveInfinity(f) || Double.IsNaN(f)) { writer.WriteString("null"); return; } string dblString = CBORUtilities.DoubleToString(f); writer.WriteString( CBORObject.TrimDotZero(dblString)); return; } writer.WriteString(flo.ToString()); return; } case CBORObject.CBORObjectTypeByteString: { var byteArray = (byte[])thisItem; if (byteArray.Length == 0) { writer.WriteString("\"\""); return; } writer.WriteCodePoint((int)'\"'); if (obj.HasTag(22)) { Base64.WriteBase64( writer, byteArray, 0, byteArray.Length, false); } 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 { Base64.WriteBase64URL( writer, byteArray, 0, byteArray.Length, false); } writer.WriteCodePoint((int)'\"'); break; } case CBORObject.CBORObjectTypeTextString: { var thisString = (string)thisItem; if (thisString.Length == 0) { writer.WriteString("\"\""); return; } writer.WriteCodePoint((int)'\"'); WriteJSONStringUnquoted(thisString, writer); writer.WriteCodePoint((int)'\"'); break; } case CBORObject.CBORObjectTypeArray: { var first = true; writer.WriteCodePoint((int)'['); foreach (CBORObject i in obj.AsList()) { if (!first) { writer.WriteCodePoint((int)','); } WriteJSONToInternal(i, writer); first = false; } writer.WriteCodePoint((int)']'); break; } case CBORObject.CBORObjectTypeExtendedRational: { var dec = (ERational)thisItem; EDecimal f = dec.ToEDecimalExactIfPossible( EContext.Decimal128.WithUnlimitedExponents()); if (!f.IsFinite) { writer.WriteString("null"); } else { writer.WriteString(f.ToString()); } break; } case CBORObject.CBORObjectTypeMap: { var first = true; var hasNonStringKeys = false; IDictionary<CBORObject, CBORObject> objMap = obj.AsMap(); foreach (KeyValuePair<CBORObject, CBORObject> entry in objMap) { CBORObject key = entry.Key; if (key.ItemType != CBORObject.CBORObjectTypeTextString) { hasNonStringKeys = true; break; } } if (!hasNonStringKeys) { writer.WriteCodePoint((int)'{'); foreach (KeyValuePair<CBORObject, CBORObject> entry in objMap) { CBORObject key = entry.Key; CBORObject value = entry.Value; if (!first) { writer.WriteCodePoint((int)','); } writer.WriteCodePoint((int)'\"'); WriteJSONStringUnquoted((string)key.ThisItem, writer); writer.WriteCodePoint((int)'\"'); writer.WriteCodePoint((int)':'); WriteJSONToInternal(value, writer); 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 objMap) { CBORObject key = entry.Key; CBORObject value = entry.Value; string str = (key.ItemType == CBORObject.CBORObjectTypeTextString) ? ((string)key.ThisItem) : key.ToJSONString(); 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); writer.WriteCodePoint((int)'\"'); writer.WriteCodePoint((int)':'); WriteJSONToInternal(value, writer); first = false; } writer.WriteCodePoint((int)'}'); } break; } default: throw new InvalidOperationException("Unexpected item type"); } }
internal static void WriteJSONStringUnquoted( string str, StringOutput sb) { // Surrogates were already verified when this // string was added to the CBOR object; that check // is not repeated here 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 >= 0x85 && (c == 0x2028 || c == 0x2029 || c == 0x85 || 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 // TODO: Also include 0x7f..0x9f in version 3 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 >= 0x2028) { 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 (!first) { if ((c & 0xfc00) == 0xd800) { sb.WriteString(str, i, 2); ++i; } else { sb.WriteCodePoint((int)c); } } } if (first) { sb.WriteString(str); } }
internal static void WriteJSONStringUnquoted( string str, StringOutput sb, JSONOptions options) { var i = 0; for (; i < str.Length; ++i) { char c = str[i]; if (c < 0x20 || c >= 0x7f || c == '\\' || c == '"') { sb.WriteString(str, 0, i); break; } } if (i == str.Length) { sb.WriteString(str, 0, i); return; } for (; i < str.Length; ++i) { char c = str[i]; if (c == '\\' || c == '"') { 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 (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) { // Replace unpaired surrogate with U+FFFD sb.WriteCodePoint(0xfffd); } else { throw new CBORException("Unpaired surrogate in string"); } } else { sb.WriteString(str, i, 2); ++i; } } else { sb.WriteCodePoint((int)c); } } }
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"); } }
internal static void WriteJSONToInternal( CBORObject obj, StringOutput writer) { int type = obj.ItemType; object thisItem = obj.ThisItem; switch (type) { case CBORObject.CBORObjectTypeSimpleValue: { if (obj.IsTrue) { writer.WriteString("true"); return; } if (obj.IsFalse) { writer.WriteString("false"); return; } writer.WriteString("null"); return; } case CBORObject.CBORObjectTypeSingle: { var f = (float)thisItem; if (Single.IsNegativeInfinity(f) || Single.IsPositiveInfinity(f) || Single.IsNaN(f)) { writer.WriteString("null"); return; } writer.WriteString( CBORObject.TrimDotZero( CBORUtilities.SingleToString(f))); return; } case CBORObject.CBORObjectTypeDouble: { var f = (double)thisItem; if (Double.IsNegativeInfinity(f) || Double.IsPositiveInfinity(f) || Double.IsNaN(f)) { writer.WriteString("null"); return; } string dblString = CBORUtilities.DoubleToString(f); writer.WriteString( CBORObject.TrimDotZero(dblString)); return; } case CBORObject.CBORObjectTypeInteger: { var longItem = (long)thisItem; writer.WriteString(CBORUtilities.LongToString(longItem)); return; } case CBORObject.CBORObjectTypeBigInteger: { writer.WriteString( CBORUtilities.BigIntToString((EInteger)thisItem)); return; } case CBORObject.CBORObjectTypeExtendedDecimal: { var dec = (EDecimal)thisItem; if (dec.IsInfinity() || dec.IsNaN()) { writer.WriteString("null"); } else { writer.WriteString(dec.ToString()); } return; } case CBORObject.CBORObjectTypeExtendedFloat: { var flo = (EFloat)thisItem; if (flo.IsInfinity() || flo.IsNaN()) { writer.WriteString("null"); return; } if (flo.IsFinite && flo.Exponent.Abs().CompareTo((EInteger)2500) > 0) { // Too inefficient to convert to a decimal number // from a bigfloat with a very high exponent, // so convert to double instead double f = flo.ToDouble(); if (Double.IsNegativeInfinity(f) || Double.IsPositiveInfinity(f) || Double.IsNaN(f)) { writer.WriteString("null"); return; } string dblString = CBORUtilities.DoubleToString(f); writer.WriteString( CBORObject.TrimDotZero(dblString)); return; } writer.WriteString(flo.ToString()); return; } case CBORObject.CBORObjectTypeByteString: { var byteArray = (byte[])thisItem; if (byteArray.Length == 0) { writer.WriteString("\"\""); return; } writer.WriteCodePoint((int)'\"'); if (obj.HasTag(22)) { Base64.WriteBase64( writer, byteArray, 0, byteArray.Length, false); } 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 { Base64.WriteBase64URL( writer, byteArray, 0, byteArray.Length, false); } writer.WriteCodePoint((int)'\"'); break; } case CBORObject.CBORObjectTypeTextString: { var thisString = (string)thisItem; if (thisString.Length == 0) { writer.WriteString("\"\""); return; } writer.WriteCodePoint((int)'\"'); WriteJSONStringUnquoted(thisString, writer); writer.WriteCodePoint((int)'\"'); break; } case CBORObject.CBORObjectTypeArray: { var first = true; writer.WriteCodePoint((int)'['); foreach (CBORObject i in obj.AsList()) { if (!first) { writer.WriteCodePoint((int)','); } WriteJSONToInternal(i, writer); first = false; } writer.WriteCodePoint((int)']'); break; } case CBORObject.CBORObjectTypeExtendedRational: { var dec = (ERational)thisItem; EDecimal f = dec.ToEDecimalExactIfPossible( EContext.Decimal128.WithUnlimitedExponents()); if (!f.IsFinite) { writer.WriteString("null"); } else { writer.WriteString(f.ToString()); } break; } case CBORObject.CBORObjectTypeMap: { var first = true; var hasNonStringKeys = false; IDictionary <CBORObject, CBORObject> objMap = obj.AsMap(); foreach (KeyValuePair <CBORObject, CBORObject> entry in objMap) { CBORObject key = entry.Key; if (key.ItemType != CBORObject.CBORObjectTypeTextString) { hasNonStringKeys = true; break; } } if (!hasNonStringKeys) { writer.WriteCodePoint((int)'{'); foreach (KeyValuePair <CBORObject, CBORObject> entry in objMap) { CBORObject key = entry.Key; CBORObject value = entry.Value; if (!first) { writer.WriteCodePoint((int)','); } writer.WriteCodePoint((int)'\"'); WriteJSONStringUnquoted((string)key.ThisItem, writer); writer.WriteCodePoint((int)'\"'); writer.WriteCodePoint((int)':'); WriteJSONToInternal(value, writer); 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 objMap) { CBORObject key = entry.Key; CBORObject value = entry.Value; string str = (key.ItemType == CBORObject.CBORObjectTypeTextString) ? ((string)key.ThisItem) : key.ToJSONString(); 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); writer.WriteCodePoint((int)'\"'); writer.WriteCodePoint((int)':'); WriteJSONToInternal(value, writer); first = false; } writer.WriteCodePoint((int)'}'); } break; } default: throw new InvalidOperationException("Unexpected item type"); } }
internal static void WriteJSONStringUnquoted( string str, StringOutput sb) { // Surrogates were already verified when this // string was added to the CBOR object; that check // is not repeated here 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 (!first) { if ((c & 0xfc00) == 0xd800) { sb.WriteString(str, i, 2); ++i; } else { sb.WriteCodePoint((int)c); } } } if (first) { sb.WriteString(str); } }
internal static void WriteJSONToInternal( CBORObject obj, StringOutput writer, JSONOptions options) { 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: { var 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: { var thisString = obj.AsString(); if (thisString.Length == 0) { writer.WriteString("\"\""); return; } writer.WriteCodePoint((int)'\"'); WriteJSONStringUnquoted(thisString, writer, options); writer.WriteCodePoint((int)'\"'); break; } case CBORType.Array: { var first = true; writer.WriteCodePoint((int)'['); foreach (CBORObject i in obj.AsList()) { if (!first) { writer.WriteCodePoint((int)','); } WriteJSONToInternal(i, writer, options); first = false; } writer.WriteCodePoint((int)']'); break; } case CBORType.Map: { var first = true; var hasNonStringKeys = false; IDictionary <CBORObject, CBORObject> objMap = obj.AsMap(); foreach (KeyValuePair <CBORObject, CBORObject> entry in objMap) { 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 objMap) { 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)':'); WriteJSONToInternal(value, writer, options); 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 objMap) { CBORObject key = entry.Key; CBORObject value = entry.Value; string str = (key.Type == CBORType.TextString) ? key.AsString() : key.ToJSONString(); if (stringMap.ContainsKey(str)) { throw new CBORException("Duplicate JSON string equivalents of map keys"); } 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)':'); WriteJSONToInternal(value, writer, options); first = false; } writer.WriteCodePoint((int)'}'); } break; } default: throw new InvalidOperationException("Unexpected item type"); } }