1

So the company I've been working for has asked me to make a C# library for a client company who needs to access our API with .NET. So far I have a working library, but I keep having problems parsing optional properties. We use mongoDB on the backend, and mongoDB doesn't store properties that aren't provided even if they're defined in the schema.

For example, my schema may look like:

{
   name:String,
   id:Number,
   phone:String,
   email:String,
   experience:[]
}

Where as I make the document:

{
   name:"joe",
   id:5,
   phone:"222-222-2222",
}

The properties email and experience DO NOT exist in my document, so my JSON looks exactly as displayed above. These values are not required values however, but I still need to parse the rest of it. The problem is that when I parse the code above for all the possible values when I parse for email or experience the parser throws a Null Reference Exception, and with good reason because the value I'm trying to parse does not exist and the way I reference these values is like so:

JObject o=JObject.Parse(json); //parse json to JObject json object
string name=(string)o["name"];
int id=(int)o["id"];
string phone=(string)o["phone"];
string email=(string)o["emain"];
List<string> exp=o["experience"].Select(t => (string)t).ToList();

Now my code is a lot more objective, I using LINQ to create an object called Job and a object Jobs to store the JObject in a way where you can query certain values out of it using methods of the Jobs object thats initialized with the original JSON string.

Thing is the only way I can think of to handle optional properties in the JSON is to try/catch each and every value. This seems very sloppy and the JSON I have to parse is about 40-50 properties in total. This seems like it would be extremely slow and a huge mess of code. I'm wondering if I can implement this is a cleaner more efficient way.

tsturzl
  • 3,089
  • 2
  • 22
  • 35
  • 1
    Have you tried `var job = JsonConvert.DeserializeObject(json);` ? – L.B Mar 10 '14 at 21:52
  • I'm not sure I understand. MongoDB is schemaless, right? You shouldn't have to parse anything. You can just dump it as BSON document into Mongo – Lews Therin Mar 10 '14 at 21:52
  • @mason it is Json.Net's class. – L.B Mar 10 '14 at 21:53
  • @mason LOL, JObject is part of Json.net – Adween Mar 10 '14 at 21:53
  • http://stackoverflow.com/questions/5624934/convert-string-into-mongodb-bsondocument – Lews Therin Mar 10 '14 at 21:56
  • Ah. I've always parsed directly to my own custom class. Why parse it to a JObject class instead of a custom object that you can control? For example, make use of the [NullValueAttribute](http://james.newtonking.com/json/help/index.html?topic=html/SerializationAttributes.htm) attribute? Use `NullValueHandling.Ignore`. – mason Mar 10 '14 at 21:56
  • @LewsTherin On the backend we use Mongoose with Node.js, so we define Schema's. I think you're misinterpreting me. I'm not connecting my client to MongoDB directly. I'm connecting to an API which is serving me content from MongoDB as JSON over HTTP. – tsturzl Mar 10 '14 at 21:57
  • Now I'm confused. You have Mongoose and MongoDB? Which is it? And no, I wasn't misinterpreting you. – Lews Therin Mar 10 '14 at 22:00
  • @mason as I specified I am doing exactly that. To parse JSON into an object, you must first define that object as this cannot be done on the fly. I have an object called `Job` this is what I parse my JSON to. However, you can't simply just parse straight to an object like you can when with Javascript. You have to use something like LINQ to grab the values and add them your your object instance. Especially when the JSON I'm retrieving has embedded objects and Arrays. Arrays need to be converted to lists because they aren't a defined length in JS. – tsturzl Mar 10 '14 at 22:00
  • @LewsTherin you obviously are, Mongoose is a library to connecting to MongoDB with Node.js. – tsturzl Mar 10 '14 at 22:01
  • I am not connecting to Mongo with C# directly. I am connecting to a RESTful API where JSON is served from a database. What you are mentionaing LewsTherin has nothing to do with my problems. I am getting JSON which I do not know for certain whether or not it has certain values/properties because these properties are not required, however when I parse for these values and they are not there I get an error. Using try/catch is very messy when you have to try/catch ~50 values out of JSON where are 90% of them are embedded in an array, object, or both. – tsturzl Mar 10 '14 at 22:03
  • Ok I guess I'm confused what layer the C# code lies in. You client connects to your API (presumably in C#), which uses Mongoose to connect to MongoDB with NodeJS... Yeah, I'm totally lost. – Lews Therin Mar 10 '14 at 22:04
  • No, you can go directly to a Job without having to convert to a JObject. See @evanmcdonnal's answer for an example of that. I'm looking at your code, and it never shows you've converted from the JObject to Job. – mason Mar 10 '14 at 22:04
  • 1
    @tsturzl It seems you are missing something. **goto first_comment;** – L.B Mar 10 '14 at 22:04
  • Thats because this code is literally 200 lines long, and I just wanted to simplify it. I didnt' want to have to define a Job object, seeing as this IS NOT WHERE MY PROBLEM LIES. – tsturzl Mar 10 '14 at 22:05
  • @tsturzl I gave you a link that shows you how it is done. But have you seen the answer to your question? By evan? – Lews Therin Mar 10 '14 at 22:05
  • If MongoDB or Mongoose aren't important to the question, then don't mention them. Just say you have some JSON that you're getting from an API. Mentioning MongoDB and Mongoose seems to lead everyone on a red herring chase. – mason Mar 10 '14 at 22:05
  • 1
    @tsturzl Your problem lies in that you're trying to manually deserialize then this is something Json.NET does out of the box, as in the the first comment and evan's answer. – Ben Aaronson Mar 10 '14 at 22:07
  • @L.B Can you explain to me how that method works exactly or where to find information on it? It seems relatively undocumented? – tsturzl Mar 10 '14 at 22:08
  • It's the most common way of deserializing. See [Json.NET documentation](http://james.newtonking.com/json/help/html/SerializingJSON.htm) (It's on line 20) – mason Mar 10 '14 at 22:10
  • @tsturzl http://james.newtonking.com/json/help/index.html?topic=html/SerializingJSON.htm – Ben Aaronson Mar 10 '14 at 22:10
  • I didn't see the answer below. I just had to refresh. – tsturzl Mar 10 '14 at 22:11

1 Answers1

7

Unless there is some good reason you can't use the generic deserialize/serialize methods I would recommend changing your approach to use those. In general, I think the type of conditional, property specific parsing you're doing above is very poor practice. Here is an example;

public class Job
{
   public string name;
   public string id;
   public string phone;
   public string email;
   public string[] experience; // can also be a List<string> without any problems
}

Job j = JsonConvert.DeserializeObject<Job>(jsonString);

string output = JsonConvert.SerializeObject(j);
//will include "optional" parameters, meaning if there is no phone value it will be an empty string but the property name will still be there.

If you really want to confirm that say, some required parameters are included but some other optional ones are not I would recommend using json schemas in combination with json.NET. You can do something like the following;

//schema in flat text file I read with File.ReadAllText(path);
{
    "type":"object",
    "$schema": "http://json-schema.org/draft-03/schema",
    "required":true,
    "properties":{
        "name": { "type":"string", "required":true },
        "id": { "type":"string", "required":true },
                "phone": { "type":"string", "required":false },
                "email": { "type":"string", "required":false },
                "experience": { "type":"array", "required":true, "items": { "string" } }
    }
}

Then in code you have something like;

JObject obj = JObject.Parse(json);
JsonSchema jscheme = JsonSchema.Parse(File.ReadAllText(thatSchemaAbove));
IList<string> errors;
obj.IsValid(jscheme, out errors);
if (errors.Count() > 0)
{
     //json didn't match the schema, do something about it!
}

EDIT: Handling complex objects;

Pretend your json is instead an object with an array of Job objects called jobs. Use the following C# class definitions for that;

public class jobsWrapper
{
    public List<Job> jobs;
}

Any construct used in json has a C# equivalent, you just need to decompose it and determine what that is.

evanmcdonnal
  • 46,131
  • 16
  • 104
  • 115
  • Thank you C# is definitely not my strong point. If I were to want to set experience to a List instead of an array would that be a problem? – tsturzl Mar 10 '14 at 22:14
  • @tsturzl not at all. Just make it so in your class and everything will work just the same. – evanmcdonnal Mar 10 '14 at 22:15
  • How would I be able to parse embedded objects? I have objects embedded in arrays, and objects inside other objects. This JSON is pretty complex. – tsturzl Mar 10 '14 at 22:22
  • 1
    @tsturzl so it's just about translating the data model in the json to classes in C#. I'll edit with another little snippet that might get you moving in the right direction. I would include more detail but it's very tedious and I can't copy/paste any of actual work for obvious reasons. – evanmcdonnal Mar 10 '14 at 22:25