17

I have json like this

{
    "name": "somenameofevent",
    "type": "event",
    "data": {
        "object": {
            "age": "18",
            "petName": "18"
        },
        "desct": {
        }
    }
}

and I have 2 objects like this

public class CustEvent
{
    [JsonProperty("name")]
    public string Name { get; set; }
    [JsonProperty("type")]
    public string EventType{ get; set; }
    [JsonProperty("data")]
    public SomeData Data{ get; set; }
}

public class SomeData
{
    [JsonProperty("object")]
    public String SomeObject { get; set;}
    [JsonProperty("dsct")]
    public String SomeDesct { get; set; }
}

I use to parse json to object Newtonsoft.NET library. And how i can get RAW JSON into SomeObject , SomeDesct properties ? In JSON "data.object ..." are complex object and i want to get only RAW JSON String to those properties. Can you help me ?

fero
  • 6,050
  • 1
  • 33
  • 56
dscherbak
  • 171
  • 1
  • 1
  • 4

6 Answers6

28

You don't need to write any converters, just use the JRaw type as follows:

public class SomeData
{
    [JsonProperty("object")]
    public JRaw SomeObject { get; set;}
    [JsonProperty("dsct")]
    public JRaw SomeDesct { get; set; }
}

Then you can access the raw value by checking the .Value property:

var rawJsonDesct = (string)data.SomeDesct.Value;

If you want to retain the string signature, just serialize the JSON to a hidden property and have the string conversion in the accessor call instead.

chakrit
  • 61,017
  • 25
  • 133
  • 162
  • Definitely the easiest solution. – sroj Jan 21 '16 at 03:05
  • This doesn't keep indent, any solution to that? – Ming Nov 27 '19 at 17:25
  • 1
    Important Note: `JRaw` doesn't preserve the original data. It normalizes strings, removes whitespace and truncates long decimals (losing potentially significant data). – CodesInChaos May 25 '20 at 14:23
  • I don't know why this option is not mentioned more. It seems like the best solution, for when you want to not parse part of an object (typically a payload that you want to keep as a string for later use). – NightCabbage Aug 25 '21 at 02:10
8

You have to write a custom converter class (derived from Newtonsoft.Json.JsonConverter) which instructs the deserializer to read the whole object and to return the JSON string for the object.

Then you have to decorate the property with the JsonConverter attribute.

[JsonConverter(typeof(YourCustomConverterClass))]
public string SomeObject { get; set; }

There are good tutorials on the web on how to create custom converters, but - for your convenience - the core of your converter might look like this:

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
    return JObject.Load(reader).ToString();
}

This method reads a complete JSON object but returns the serialized version of the object as string. There is a bit of overhead because the object is deserialized to a JObject and then serialized again, but for me it's the easiest way to do this. Maybe you have a better idea.

Benjamin
  • 10,085
  • 19
  • 80
  • 130
fero
  • 6,050
  • 1
  • 33
  • 56
  • A more efficient ReadJson could use the same technique as JRaw uses internally. See implementation of ReadJson in this answer: https://stackoverflow.com/a/17601556/27846 – pettys May 11 '23 at 22:45
4

I use this implementation of the custom JsonConverter.

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

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var sb = new StringBuilder();
        JsonToken previousToken = JsonToken.None;

        if (reader.TokenType == JsonToken.StartObject)
        {
            sb.Append('{');
            int depth = 1;
            while (depth > 0)
            {
                if (!reader.Read())
                    break;
                switch (reader.TokenType)
                {
                    case JsonToken.PropertyName:
                        if (previousToken == JsonToken.Boolean || previousToken == JsonToken.Integer || previousToken == JsonToken.Float)
                            sb.Append(',');
                        sb.AppendFormat("\"{0}\":", reader.Value);
                        break;
                    case JsonToken.StartArray:
                        if (previousToken == JsonToken.EndArray)
                            sb.Append(',');
                        sb.Append('[');
                        break;
                    case JsonToken.Boolean:
                    case JsonToken.Integer:
                    case JsonToken.Float:
                        if (previousToken == JsonToken.Boolean || previousToken == JsonToken.Integer || previousToken == JsonToken.Float)
                            sb.Append(',');
                        sb.AppendFormat(System.Globalization.CultureInfo.InvariantCulture, "{0}", reader.Value);
                        break;
                    case JsonToken.EndArray:
                        sb.Append(']');
                        break;
                    case JsonToken.StartObject:
                        sb.Append('{');
                        depth++;
                        break;
                    case JsonToken.EndObject:
                        sb.Append('}');
                        depth--;
                        break;
                }
                previousToken = reader.TokenType;
            }
        }
        return sb.ToString();
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        writer.WriteRawValue(value.ToString());
    }

    public override bool CanWrite
    {
        get
        {
            return true;
        }
    }
}
eLVik
  • 741
  • 7
  • 14
2

If you are worried about the overhead because the object is deserialized to a JObject a and then serialized again (solution offered by @fero ) then you can try the following.

Approach 1: Create your own custom JsonConverter and override ReadJson

using(var jsonReader = new JsonTextReader(myTextReader))
{
  while(jsonReader.Read()){
    if(jsonReader.TokenType.PropertyName=="SomeDesct")
    {
      //do what you want
    } 
  }
}

For more detail check the link Incremental JSON Parsing in C#

Approach 2: Read the json string and apply string functions or regex function to get the desired string.

Community
  • 1
  • 1
keyr
  • 990
  • 13
  • 27
1

As chakrit suggested, you can do something like this to provide a string signature for your object, while using JRaw to do the real work.

    [JsonProperty("Data")]
    public JRaw Raw { get; set; }

    [JsonIgnore]
    public string Data
    {
        get => Raw?.Value as string;
        set => Raw = new JRaw(value);
    }
John Fisher
  • 22,355
  • 2
  • 39
  • 64
  • Or the opposite `[JsonIgnore] public string Data; [JsonProperty("Data")] public JRaw DataRaw => new JRaw(Data);` works pretty well and doesn't need type conversions when using the data in other ways. – Qurben Jul 26 '18 at 13:40
0

In your case you can directly use the static method from JsonConvert class

PopulateObject(string value, object target, JsonSerializerSettings settings);

Sunil S
  • 391
  • 3
  • 5