5

I have some JSON from a third party system that contains backslashes in the value. For example:

string extract = @"{""key"": ""\/Date(2015-02-02)\/""}";

which without the c# string escaping corresponds to the string:

{"key": "\/Date(2015-02-02)\/"}

I'd like to be able to format (e.g. indent) this JSON.

Typically for formatting, I might use something like JsonConvert like so:

JsonConvert.SerializeObject(JsonConvert.DeserializeObject(extract), Formatting.Indented)

This doesn't quite work, as it sees the value as a date, but as it's not in the standard MS format of \/Date(ticks)\/, it goes to a date of 1 Jan 1970:

{
  "key": "1970-01-01T00:00:02.015+00:00"
}

Next approach is to use the serializer settings to not convert dates (I'm not bothered whether it recognises the field as a date, although it would probably be handy later on):

JsonSerializerSettings settings = new JsonSerializerSettings
{
    DateParseHandling = DateParseHandling.None,
};

JsonConvert.SerializeObject(JsonConvert.DeserializeObject(extract, settings), Formatting.Indented);

This appears to have treated the backslash as an escape character during the deserialization, so it is "lost" once I see the final result:

{
  "key": "/Date(2015-02-02)/"
}

Is there a way that I can format the JSON in C# (with or without JsonConvert), that will preserve the backslash in the value?

Note that the real JSON I am dealing with is (a) reasonably large, but not too large for some regex/find-replace solution, if really necessary (b) not under my control, so I can't change the format. I'm sure the answer is already on StackOverflow, but I'm finding it difficult to find the right search terms...

1
  • in c# double backslash is used for representing backslash into a string Commented Apr 7, 2016 at 11:35

2 Answers 2

4

Have you tried:

extract = extract.Replace("\\","\\\\");

before parsing the string?

Sign up to request clarification or add additional context in comments.

2 Comments

This works, but feels messy. To be precise, the following does the job - undoing the Replace at the end: JsonConvert.SerializeObject(JsonConvert.DeserializeObject(extract.Replace("\\", "\\\\"), settings), Formatting.Indented).Replace("\\\\", "\\")
Yes, it's a little messy, but, taking into accout the complexity of the input, I suspect you'll be forced to use this solution or something similar. Backslashes are sometimes hard to serialize/parse.
2

The basic problem is that, in a JSON string literal, the escaped solidus "\/" means exactly the same as the unescaped solidus "/", and Json.NET parses and interprets this escaping at a very low level, namely JsonTextReader.ReadStringIntoBuffer(). Thus there's no way for higher level code to detect and remember whether a string literal was formatted as "\/Date(2015-02-02)\/" or "/Date(2015-02-02)/" and later write back one or the other as appropriate.

If you are OK with always adding the extra escaping to strings that start with /Date( and end with )/, you can use a custom subclass of JsonTextWriter to do this:

public class DateLiteralJsonTextWriter : JsonTextWriter
{
    public DateLiteralJsonTextWriter(TextWriter writer) : base(writer) { }

    public override void WriteValue(string value)
    {
        const string startToken = @"/Date(";
        const string replacementStartToken = @"\/Date(";
        const string endToken = @")/";
        const string replacementEndToken = @")\/";

        if (value != null && value.StartsWith(startToken) && value.EndsWith(endToken))
        {
            var sb = new StringBuilder();

            // Add the initial quote.
            sb.Append(QuoteChar);

            // Add the new start token.
            sb.Append(replacementStartToken);

            // Add any necessary escaping to the innards of the "/Date(.*)/" string.
            using (var writer = new StringWriter(sb))
            using (var jsonWriter = new JsonTextWriter(writer) { StringEscapeHandling = this.StringEscapeHandling, Culture = this.Culture, QuoteChar = '\"' })
            {
                var content = value.Substring(startToken.Length, value.Length - startToken.Length - endToken.Length);
                jsonWriter.WriteValue(content);
            }

            // Strip the embedded quotes from the above.
            sb.Remove(replacementStartToken.Length + 1, 1);
            sb.Remove(sb.Length - 1, 1);

            // Add the replacement end token and final quote.
            sb.Append(replacementEndToken);
            sb.Append(QuoteChar);

            // Write without any further escaping.
            WriteRawValue(sb.ToString());
        }
        else
        {
            base.WriteValue(value);
        }
    }
}

Then parse with DateParseHandling = DateParseHandling.None as you are currently doing:

var settings = new JsonSerializerSettings { DateParseHandling = DateParseHandling.None };

var sb = new StringBuilder();
using (var writer = new StringWriter(sb))
using (var jsonWriter = new DateLiteralJsonTextWriter(writer) { Formatting = Formatting.Indented})
{
    JsonSerializer.CreateDefault(settings).Serialize(jsonWriter,  JsonConvert.DeserializeObject(extract, settings));
}

Console.WriteLine(sb);

This prints:

{
  "key": "\/Date(2015-02-02)\/"
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.