0

I've got a file with JSON content like this:

{
  "Data": {
    "A": {
      "A1": "S",
      "A2": 0.0
    }
  },
  "NoData": "text"
}

What I want to achieve is to get the inner json of "Data" as a string. I do not want it to be deserialized as an object. I already tried to deserialize it with Json.NET JsonConvert.DeserializeObject<TestClass> where Test class is defined as

class Testclass
{
  public string Data {get; set;}
  public string NoData {get; set;}
}

But this results in Newtonsoft.Json.JsonReaderException: Unexpected character encountered while parsing value: {. Path 'Data', line 2, position 11.

The data in "Data" is cryptographically signed by a remote server and the signature needs to be checked. But I do not necessarily have the same classes the data was serialized with at the clients side. That's why I want to do as few conversions as possible. So deserializing and serializing again may change the original string in "data". All spaces and linebreaks have to stay intact.

Norman
  • 3,279
  • 3
  • 25
  • 42
  • take a look here: https://stackoverflow.com/a/29988081/7968203 – Isparia Feb 18 '20 at 11:39
  • 1
    Does this answer your question? [Deserialize JSON object property to string](https://stackoverflow.com/questions/29980580/deserialize-json-object-property-to-string) – Isparia Feb 18 '20 at 11:40
  • Maybe you can deserialize `Data` from JSON to `internal object DataInternal { get; set; }`, and then get it with `public string Data { get => JsonConvert.SerializeObject(DataInternal); }` – vasily.sib Feb 18 '20 at 11:40
  • 1
    _"I do not want it to be deserialized as an object"_ - can you explain why not? – CodeCaster Feb 18 '20 at 11:42
  • The data in "Data" is cryptographically signed by a remote server and the signature needs to be checked. But I do not necessarily have the same classes the data was serialized with at the clients side. That's why I want to do as few conversions as possible. – Norman Feb 18 '20 at 11:55

3 Answers3

2
 {
    "A": {
      "A1": "S",
      "A2": 0.0
    }
  }

is a JSON Object Use JToken datatype

class JsonConverterObjectToString : JsonConverter
    {
        public override bool CanConvert(Type objectType)
        {
            return (objectType == typeof(JTokenType));
        }

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            JToken token = JToken.Load(reader);
            if (token.Type == JTokenType.Object)
            {
                return token.ToString();
            }
            return null;
        }

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            //serializer.Serialize(writer, value);

            //serialize as actual JSON and not string data
            var token = JToken.Parse(value.ToString());
            writer.WriteToken(token.CreateReader());

        }
    }

Otherwise use JsonConverter like this

  JsonConvert.DeserializeObject<Testclass>(json, 
                new JsonConverterObjectToString());
umasankar
  • 599
  • 1
  • 9
  • 28
  • ReadJson technically deserializes and serializes again. Correct?! This may modify the original json in the "data" property depending on how the data is serialized again? – Norman Feb 18 '20 at 11:58
0
    using Newtonsoft.Json;
    using Newtonsoft.Json.Linq;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;

    namespace ConsoleApp5
    {
        class Testclass
        {
            public string Data { get; set; }
            public string NoData { get; set; }
        }
        class JsonConverterObjectToString : JsonConverter
        {
            public override bool CanConvert(Type objectType)
            {
                return (objectType == typeof(string));
            }

            public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
            {
                JToken token = JToken.Load(reader);
                if (token.Type == JTokenType.Object)
                {
                    return token.ToString();
                }
                return null;
            }

            public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
            {
                //serializer.Serialize(writer, value);

                //serialize as actual JSON and not string data
                var token = JToken.Parse(value.ToString());
                writer.WriteToken(token.CreateReader());

            }
        }
        class Program
        {

            static void Main(string[] args)
            {
                var json = "{\r\n  \"Data\": {\r\n    \"A\": {\r\n      \"A1\": \"S\",\r\n      \"A2\": 0.0\r\n    }\r\n  },\r\n  \"NoData\": \"text\"\r\n}";
                var jObj = JsonConvert.DeserializeObject<Testclass>(json, new JsonConverterObjectToString());
                var jObjStrin = JsonConvert.SerializeObject(jObj, new JsonConverterObjectToString());
 Console.WriteLine(jObjString);
            }
        }
    }

we can able to JToken to string with the same format Please check the image. the Data has serialized again the same format using the same Convertor

enter image description here

umasankar
  • 599
  • 1
  • 9
  • 28
0

I had implemented a naive JSON parser just for solving this specific problem, here's the code:

Method:

static string ExtractInnerJsonObject(ReadOnlySpan<byte> jsonPayload, ReadOnlySpan<char> targetField, bool preserveWhiteSpaces = false)
{
    const int TYPE_OTHER = 0;
    const int TYPE_OBJECT = 1;
    const int TYPE_STRING = 2;
    const int TYPE_ARRAY = 3;

    ReadOnlySpan<char> jsonPayloadString = Encoding.UTF8.GetString(jsonPayload).AsSpan();

    int objectDepth = 0;
    int lastWhiteSpacePosition = 0;
    int fieldNameStartPosition = 0;
    int fieldNameEndPosition = 0;
    int valueStartPosition = 0;
    int valueEndPosition = 0;
    int detectedValueType = TYPE_OTHER;

    bool isValueTypeDetected = false;

    bool isValueScanStarted = false;
    bool isValueScanCompleted = false;

    bool isTargetField = false;
    bool isFieldNameScanStarted = false;
    bool isFieldNameScanCompleted = false;


    for (int i = 0; i < jsonPayloadString.Length; i++)
    {
        char c = jsonPayloadString[i];

        if (c == '{')
        {
            objectDepth++;

            if (isValueScanStarted && !isValueScanCompleted && !isValueTypeDetected)
            {
                detectedValueType = TYPE_OBJECT;
                isValueTypeDetected = true;
            }
        }
        else if (c == '}')
        {
            objectDepth--;

            if (isValueScanStarted && !isValueScanCompleted && detectedValueType == TYPE_OBJECT && objectDepth == 1)
            {
                valueEndPosition = i;
                isValueScanCompleted = true;
            }

            if (isValueScanStarted && !isValueScanCompleted && detectedValueType == TYPE_OTHER)
            {
                valueEndPosition = i - 1;
                isValueScanCompleted = true;
            }
        }
        else if (c == '"')
        {
            if (!isValueScanStarted)
            {
                if (!isFieldNameScanStarted)
                {
                    fieldNameStartPosition = i + 1;
                    isFieldNameScanStarted = true;
                }
                else
                {
                    fieldNameEndPosition = i - 1;
                    isFieldNameScanCompleted = true;
                }
            }
            else
            {
                if (!isValueTypeDetected)
                {
                    detectedValueType = TYPE_STRING;
                    isValueTypeDetected = true;
                }
                else
                {
                    if (!isValueScanCompleted && detectedValueType == TYPE_STRING)
                    {
                        valueEndPosition = i;
                        isValueScanCompleted = true;
                    }
                }
            }
        }
        else if (c == ':')
        {
            if (!isValueScanStarted)
            {
                valueStartPosition = i + 1;
                isValueScanStarted = true;
            }
        }
        else if (c == ',')
        {
            if (isValueScanStarted && !isValueScanCompleted && detectedValueType == TYPE_OTHER)
            {
                valueEndPosition = i - 1;
                isValueScanCompleted = true;
            }
        }
        else if (c == '[')
        {
            if (isValueScanStarted && !isValueScanCompleted && !isValueTypeDetected)
            {
                detectedValueType = TYPE_ARRAY;
                isValueTypeDetected = true;
            }
        }
        else if (c == ']')
        {
            if (isValueScanStarted && !isValueScanCompleted && detectedValueType == TYPE_ARRAY)
            {
                valueEndPosition = i;
                isValueScanCompleted = true;
            }
        }
        else if (char.IsWhiteSpace(c))
        {
            lastWhiteSpacePosition = i;
        }

        if (isFieldNameScanStarted && isFieldNameScanCompleted)
        {
            ReadOnlySpan<char> fieldName = jsonPayloadString[fieldNameStartPosition..(fieldNameEndPosition + 1)];

            if (fieldName.Equals(targetField, StringComparison.Ordinal))
            {
                isTargetField = true;
            }

            isFieldNameScanStarted = false;
            isFieldNameScanCompleted = false;
        }

        if (isValueScanStarted && isValueScanCompleted)
        {
            if (isTargetField)
            {
                ReadOnlySpan<char> value = jsonPayloadString[valueStartPosition..(valueEndPosition + 1)];

                if (preserveWhiteSpaces)
                {
                    return value.ToString();
                }
                else
                {
                    return value.ToString().Trim();
                }
            }

            isValueScanStarted = false;
            isValueScanCompleted = false;
            isValueTypeDetected = false;
            detectedValueType = TYPE_OTHER;
        }

    }

    return null;
}

Usage:

byte[] jsonPayloadReceivedFromSomewhere = Encoding.UTF8.GetBytes(
@"{
  ""Data"": {
    ""A"": {
      ""A1"": ""S"",
      ""A2"": 0.0
    }
  },
  ""NoData"":             ""hello
world!!@#$%^&*()_+-=[]{}|\/?><,.`01234     56789
""
, ""TestArray"":            [1, 2, 3 , 4, 5], ""TestBoolean"" :       true   
,        ""TestDouble""       :  123.45678
, ""TestInteger"" :-9999



}");

string a = ExtractInnerJsonObject(jsonPayloadReceivedFromSomewhere, "Data");
string b = ExtractInnerJsonObject(jsonPayloadReceivedFromSomewhere, "NoData");
string c = ExtractInnerJsonObject(jsonPayloadReceivedFromSomewhere, "InvalidField");
string d = ExtractInnerJsonObject(jsonPayloadReceivedFromSomewhere, "TestArray");
string e = ExtractInnerJsonObject(jsonPayloadReceivedFromSomewhere, "TestBoolean");
string f = ExtractInnerJsonObject(jsonPayloadReceivedFromSomewhere, "TestDouble");
string g = ExtractInnerJsonObject(jsonPayloadReceivedFromSomewhere, "TestInteger");

Result:

enter image description here

DK Dhilip
  • 2,614
  • 1
  • 8
  • 17