0

I have a problem deserializing this JSON response

{
  "posts": {
    "Pippo": {
      "text": "text1",
      "link": "link1"
    },
    "Pluto": {
      "text": "text2",
      "link": "link2"
    }
  }
}

I'm using this model

public class postModel
{
    public string text { get; set; }
    public string link { get; set; }
}

public class postFields
{
    public postModel post { get; set; }
}

public class RootObject
{
    public Dictionary<string, postFields> posts { get; set; }
}

then I deserialize this way

var deserialized = JsonConvert.DeserializeObject<RootObject>(json);

then I stop. I cannot read values because I try this

foreach (var value in deserialized)
            {
                new postModel
                {
                    text = value.Value.post.text,
                    link = value.Value.post.link
                };
            }

Then I get NullReferenceException, because the name of JSON property isn't "post" but Pippo, Pluto, etc.

Can someone help me?

3 Answers3

0

This json is not parsable, because it cannot be trasnfered into any type with such "dynamic" key instead of key value pair. What you should use is JObject.

Quick sample:

JObject j = JObject.Parse(json);

var lst = j["posts"][0].Select(jp => ((JProperty)jp).Name).ToList();
Community
  • 1
  • 1
VidasV
  • 4,335
  • 1
  • 28
  • 50
  • I'm sorry I don't get this working, could you post a sample code with postModel objects creation please? – user1994860 Feb 04 '13 at 11:45
  • I have edited the sample, sorry wrong one earlier. This way you can get all the property names and later access that jobject by names. If you want detailed tutorials i suggest you google how to work with jobject – VidasV Feb 04 '13 at 15:38
0

You need the following class in your project:

namespace Newtonsoft.Json
{
    public class DictionaryConverter<tKey, tValue>: JsonConverter
    {
        public override bool CanRead { get { return true; } }
        public override bool CanWrite { get { return true; } }

        public override bool CanConvert( Type objectType )
        {
            if( !objectType.IsGenericType )
                return false;
            if( objectType.GetGenericTypeDefinition() != typeof( Dictionary<,> ) )
                return false;
            Type[] argTypes = objectType.GetGenericArguments();
            if( argTypes.Length != 2 || argTypes[ 0 ] != typeof( tKey ) || argTypes[ 1 ] != typeof( tValue ) )
                return false;
            return true;
        }

        public override object ReadJson( JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer )
        {
            do
            {
                if( JsonToken.StartObject != reader.TokenType )
                    break;
                Dictionary<tKey, tValue> res = new Dictionary<tKey, tValue>();

                while( reader.Read() )
                {
                    if( JsonToken.EndObject == reader.TokenType )
                        return res;
                    if( JsonToken.PropertyName == reader.TokenType )
                    {
                        tKey key = (tKey)Convert.ChangeType( reader.Value, typeof( tKey ), null );
                        if( !reader.Read() )
                            break;
                        tValue val = serializer.Deserialize<tValue>( reader );
                        res[ key ] = val;
                    }
                }
            }
            while( false );
            throw new Exception( "unexpected JSON" );
        }

        public override void WriteJson( JsonWriter writer, object value, JsonSerializer serializer )
        {
            if( null == value )
                return;
            Dictionary<tKey, tValue> src = value as Dictionary<tKey, tValue>;
            if( null == src )
                throw new Exception( "Expected Dictionary<{0}, {1}>".FormatWith( typeof( tKey ).Name, typeof( tValue ).Name ) );

            writer.WriteStartObject();
            foreach (var kvp in src)
            {
                string strKey = (string)Convert.ChangeType( kvp.Key, typeof( string ), null );
                writer.WritePropertyName( strKey );
                serializer.Serialize( writer, kvp.Value );
            }
            writer.WriteEndObject();
        }
    }
}

Then, you modify your RootObject in the following way:

public class RootObject
{
    [ JsonProperty, JsonConverter( typeof( DictionaryConverter<string, postModel> ) ) ]
    public Dictionary<string, postModel> posts;
}

Then everything will work as you expect.

Update: here's the utility method I've forgot:

public static class SharedUtils
{
    public static string FormatWith( this string format, params object[] args )
    {
        if( format == null )
            throw new ArgumentNullException( "format" );
        return String.Format( format, args );
    }
}
Soonts
  • 20,079
  • 9
  • 57
  • 130
0

Thanks to both of you; I solved this way:

var main = JObject.Parse(json);

        foreach (var mainRoute in main.Properties()) // this is "posts"
        {
            foreach (var subRoute in mainRoute.Values<JObject>().SelectMany(x => x.Properties())) // this is "Pippo", "Pluto"
            {
                var deserialized = JsonConvert.DeserializeObject<postModel>(subRoute.Value.ToString());

                new postModel
                {
                    text = deserialized.text,
                    link = deserialized.link
                };
            }
        }