How to serialize JSON without outputting quotes around certain values - json

The short version of problem. In some cases I want no quotes on a string JSon value:
Color =Highcharts.getOptions().colors[0]
instead of this:
Color = "Highcharts.getOptions().colors[0]"
+++++++++++++++++++++++++++++++++++++++
The long details.... I have working code that is a model I made (as proof of concept) of a controller generating all the Json for a HighCharts.com graph on the client.
1
Client code (works)
#{
ViewBag.Title = "View";
}
<h2>High Charts Proof of Concept</h2>
<div id="container" style="min-width: 310px; height: 400px; margin: 0 auto"></div>
#section Scripts {
<script type="text/javascript">
$(function() {
fetchSampleChart1();
function fetchSampleChart1() {
$.ajax({
url: '/Test1/SampleChart1/',
type: 'GET',
//data: '?',
//data: 'adminEntityID=' + adminEntity + '&fieldName=' + fieldName + '&fieldValue=' + fieldValue + '&outletID=' + outlet,
complete: function (data, textStatus, xhr) {
console.log(data.responseText);
var strGraphData = $.parseJSON(data.responseText);
if (strGraphData.length == 0) {
//GraphEmptyDisplay(parmChartId, msg);
console.log("!!!!GRAPH DATA EMPTY!!!!");
return '';
}
$("#container").highcharts(strGraphData);
},
error:function(xhr, textStatus, errorThrown) {
//Inject a default error message to the modal target.
}
});
}
});
</script>
<script type="text/javascript" src="/scripts/jquery-1.10.2.min.js"></script>
<script type="text/javascript" src="https://code.highcharts.com/highcharts.js"></script>
<script type="text/javascript" src="https://code.highcharts.com/modules/exporting.js"></script>
}
2
Controller Action it calls. Works except it emits quotes on all output where I just want it on most of output.
[HttpGet]
public ContentResult SampleChart1()
{
var sc = new HighChartGraph
{
Title = new Title() {Text = "Combination Chart"},
XAxis = new XAxis() {Categories = new string[] {"Apples", "Oranges", "Pears", "Banannas", "Plums"}},
Center = "[100,80]",
Size = 100,
ShowInLegend = false,
DataLabels = new dataLabels {Enabled = false},
Labels = new Labels
{
Items = new Items
{
Html = "Total Fruit consumption",
Style =
new Style()
{
Left = "50px",
Top = "18px",
//Color = "(Highcharts.theme && Highcharts.theme.textColor) || 'black'"
Color="yellow"
}
}
},
Series = new List<GraphSeries>
{
new GraphSeries
{
GraphType = "column",
Name = "Jane",
DataSimple = new decimal[] {3, 2, 1, 3, 4}
},
new GraphSeries
{
GraphType = "column",
Name = "John",
DataSimple = new decimal[] {2, 3, 5, 7, 6}
},
new GraphSeries
{
GraphType = "column",
Name = "Joe",
DataSimple = new decimal[] {4, 3, 3, 9, 0}
},
new GraphSeries
{
GraphType = "spline",
Name = "Average",
DataSimple = new decimal[] {3, 2.67m, 3, 6.33m, 3.33m},
Marker =
new marker
{
LineWidth = 2,
LineColor = "Highcharts.getOptions().colors[3]",
//LineColor="Black",
FillColor = "white"
}
},
new GraphSeries
{
GraphType = "pie",
Name = "Total Consumption",
DataExtended = new List<Data>
{
new Data {Name = "Jane", Y = 13, Color = "Highcharts.getOptions().colors[0]"},
//new data {Name = "Jane", Y = 13, Color = "Red"},
new Data {Name = "John", Y = 23, Color = "Highcharts.getOptions().colors[1]"},
//new data {Name = "John", Y = 23, Color = "Black"},
new Data {Name = "Joe", Y = 19, Color = "Highcharts.getOptions().colors[2]"}
//new data {Name = "Joe", Y = 19, Color = "Blue"},
}
}
}
};
//var j=new JavaScriptSerializer().Serialize(sc); // .NET Serialization will not use [JsonProperty()] attributes
var j = JsonConvert.SerializeObject(sc); // Newtonsoft Serialization WILL USE [JsonProperty()] attributes
//return Json(sc,JsonRequestBehavior.AllowGet); // cannot use it will not use our NewtonSoft serialization
return Content(j, "application/json");
}
3 Model It calls to get the JSon:
public class HighChartGraph
{
[JsonProperty(PropertyName = "title")]
public Title Title;
[JsonProperty(PropertyName = "xAxis")]
public XAxis XAxis;
[JsonProperty(PropertyName = "labels")]
public Labels Labels;
[JsonProperty(PropertyName = "center")]
public string Center;
[JsonProperty(PropertyName = "size")]
public int Size;
[JsonProperty(PropertyName = "showInLegend")]
public bool ShowInLegend;
[JsonProperty(PropertyName = "dataLabels")]
public dataLabels DataLabels;
[JsonProperty(PropertyName = "series")]
public List<GraphSeries> Series;
}
public class Title
{
[JsonProperty(PropertyName = "text")]
public string Text;
}
public class XAxis
{
[JsonProperty(PropertyName = "categories")]
public string[] Categories;
}
public class Labels
{
[JsonProperty(PropertyName = "items")]
public Items Items;
}
public class Items
{
[JsonProperty(PropertyName = "html")]
public string Html;
[JsonProperty(PropertyName = "style")]
public Style Style;
}
public class Style
{
[JsonProperty(PropertyName = "left")]
public string Left;
[JsonProperty(PropertyName = "top")]
public string Top;
[JsonProperty(PropertyName = "color")]
public string Color;
}
public class GraphSeries
{
private string _GraphType;
[JsonProperty(PropertyName = "type")]
public string GraphType
{
get { return _GraphType; }
set { _GraphType = value; }
}
[JsonProperty(PropertyName = "name")]
public string Name;
[JsonProperty(PropertyName = "marker")]
public marker Marker;
[JsonProperty(PropertyName = "data")]
public object Data
{
get
{
if (DataSimple != null) return DataSimple;
if(DataExtended !=null) return DataExtended;
return null;
}
}
[NonSerialized][ScriptIgnore]
public decimal[] DataSimple = null;
[NonSerialized][ScriptIgnore]
public List<Data> DataExtended = null;
}
public class Data
{
[JsonProperty(PropertyName = "name")]
public string Name;
[JsonProperty(PropertyName = "y")]
public long Y;
[JsonProperty(PropertyName = "color")]
public string Color;
}
public class dataLabels
{
[JsonProperty(PropertyName = "enabled")]
public bool Enabled;
}
public class marker
{
[JsonProperty(PropertyName = "linewidth")]
public int LineWidth;
[JsonProperty(PropertyName = "linecolor")]
public string LineColor;
[JsonProperty(PropertyName = "fillcolor")]
public string FillColor;
}
4 As a point of reference here is the HighCharts graph I created the model and sample code from:
http://jsfiddle.net/gh/get/jquery/1.9.1/highslide-software/highcharts.com/tree/master/samples/highcharts/demo/combo/

HighCharts isn't taking JSON but an actual javascript object. That's why they're able to have executable stuff in it. JSON is very specifically non-executable, data only, for security reasons.
You've got a couple of options here:
Use JSON as an interchange format, but have a client side function that spins through the JSON, notices if it contains an allowed executable function, eval()'s it, and overwrites the value back into the property.
Write your own JSONish generator that reflects on the object tree and builds out an executable javascript object vs. JSON
Hook into JSON.NET and make an ExecutableString type that renders without quotes. It looks like you could make a JsonConverter to do that, along the lines of: https://stackoverflow.com/a/21766191/8037. Do like that JsonConverter, except call writer.WriteRaw or writer.WriteRawValue to write out the js.
I don't have to mention that by doing this, you end run around a lot of security features. Eval'ing any code received from an external source could be dangerous. That said, if the feature is proof of concept, and if it's only contacting a server you control and can't be injected a different way, you should be pretty safe.

I would simply update public class Data so that instead of putting the string for Color and LineColor property, you have some kind of enum:
new Data {Name = "Joe", Y = 19, Color = HighchartsCcolor2 }
Then on the client, you loop through your data.responseText and switch on the .Color property. There you would use the value you want based on the enum value.
This would allow you to return valid JSON and handle it however you would like on the client.

Related

Using Newtonsoft JsonConverter to Encrypt JSON object

I'm developing a project that will require me to include credentials for things like an SMTP server. I'd like to store this information along with the complete details of the endpoint in an embedded JSON file, but I would like to have that information encrypted and then let my application decrypt it when it needs to establish a connection and log in. The JSON structure looks something like this:
{
"Endpoints" : [
{
"Endpoint" : {
"Host": "smtp.mydomain.tld",
"Port": 587,
"Username": "user#mydomain.tld",
"Password": "mYp#s$w0?d"
}
}
]
}
While what I'd really like to have actually stored in the file would look something like this:
{
"Endpoints" : [
{
"Endpoint" : "<BASE64_ENCODED_STRING>"
}
]
}
Using Newtonsoft's Json.NET, I've built the class object/properties to desriealize this structure:
<JsonProperty("Endpoints")>
Public Property Endpoints As List(Of EndpointContainer) = Nothing
Public Class EndpointContainer
<EditorBrowsable(EditorBrowsableState.Never)> <DebuggerBrowsable(DebuggerBrowsableState.Never)>
Private Const EncryptedPrefix As String = "myappcipher:"
<EditorBrowsable(EditorBrowsableState.Never)> <DebuggerBrowsable(DebuggerBrowsableState.Never)>
<JsonProperty("Endpoint")> <JsonConverter(GetType(EndpointProtector))>
Public Property Endpoint As Endpoint = Nothing
End Class
And I've built the inherited JsonConverter class ("EndpointProtector") like this:
Public Class EndpointProtector
Inherits JsonConverter
Public Sub New()
Using SHAEncryption = New SHA256Managed()
_EncryptionKey = SHAEncryption.ComputeHash(Encoding.UTF8.GetBytes(TestEncryptionKey))
End Using
End Sub
Public Overrides Sub WriteJson(writer As JsonWriter, value As Object, serializer As JsonSerializer)
Dim clearText As String = JsonConvert.SerializeObject(value)
If clearText Is Nothing Then
Throw New ArgumentNullException(NameOf(clearText))
End If
writer.WriteValue(EncryptEndpoint(clearText))
End Sub
Public Overrides Function ReadJson(reader As JsonReader, objectType As Type, existingValue As Object, serializer As JsonSerializer) As Object
Dim DecryptString As String = TryCast(reader.Value, String)
If String.IsNullOrEmpty(DecryptString) Then
Return reader.Value
ElseIf Not DecryptString.StartsWith(EncryptedPrefix, StringComparison.OrdinalIgnoreCase) Then
Return DecryptString
Else
Return DecryptEndpoint(DecryptString)
End If
End Function
Public Overrides Function CanConvert(objectType As Type) As Boolean
Throw New NotImplementedException()
End Function
End Class
Currently I have the JSON file itself with the full object definition (as in the first code block). When my application reads that JSON, it correctly moves to the overridden ReadJson() method I have, but the reader.Value is null (Nothing), so it never actually gets to the DecryptEndpoint() method. Of course, that means there's nothing to encrypt, so the application won't even step into the WriteJson() method.
I've tried a couple of variations, including making the Endpoint property into a private variable with a generic Object type, and then having a separate public property with the <JsonIgnore> decoration to "read" from that, but nothing seems to get me where I need to be. I'm sure I'm overlooking something here, but I can't seem to figure out why it's not getting anything at all.
I looked at a few other SO questions like Encrypt and JSON Serialize an object, but I've still not yet been able to figure out quite where I've gone wrong here.
NOTE: I intentionally didn't include the code for the EncryptEndpoint() or DecryptEndpoint() methods here simply because the code is never making it that far in the process. If you feel it's needed to fully answer the question, please let me know.
this is a linqpad example of working encrypt/decrypt base on JsonAttribute
void Main()
{
string str = "";
var t = new Test() { encName = "some long text some long text some long text", Name = "test" };
JsonSerializerSettings theJsonSerializerSettings = new JsonSerializerSettings();
theJsonSerializerSettings.TypeNameHandling = TypeNameHandling.None;
str = JsonConvert.SerializeObject(t, theJsonSerializerSettings).Dump();
JsonConvert.DeserializeObject<Test>(str, theJsonSerializerSettings).Dump();
}
public class Test
{
[JsonConverter(typeof(EncryptingJsonConverter))]
public string encName { get; set; }
public string Name { get; set; }
}
/// <summary>[JsonConverter(typeof(EncryptingJsonConverter), string 32byte array)]</summary>
public class EncryptingJsonConverter : JsonConverter
{
private readonly byte[] _encryptionKeyBytes;
private readonly string _encryptionKeyString;
///<summary>Key must be 32char length</summary>
public EncryptingJsonConverter()
{
string encryptionKey = "E546C8DF278CD5931069B522E695D4F2"; //get from config
if (string.IsNullOrEmpty(encryptionKey))
throw new ArgumentNullException(nameof(encryptionKey));
_encryptionKeyString = encryptionKey;
_encryptionKeyBytes = Convert.FromBase64String(encryptionKey);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var stringValue = (string)value;
if (string.IsNullOrEmpty(stringValue))
{
writer.WriteNull();
return;
}
//string enc = stringValue.Encrypt(_encryptionKeyString);
string enc = Crypto.Encrypt(stringValue, _encryptionKeyBytes);
writer.WriteValue(enc);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var value = reader.Value as string;
if (string.IsNullOrEmpty(value))
return reader.Value;
try
{
//return value.Decrypt(_encryptionKeyString);
return Crypto.Decrypt(value, _encryptionKeyBytes);
}
catch
{
return string.Empty;
}
}
/// <inheritdoc />
public override bool CanConvert(Type objectType)
{
return objectType == typeof(string);
}
}
public static class Crypto
{
public static string Encrypt(this string text, string key)
{
if (string.IsNullOrEmpty(key))
throw new ArgumentException("Key must have valid value.", nameof(key));
if (string.IsNullOrEmpty(text))
throw new ArgumentException("The text must have valid value.", nameof(text));
var buffer = Encoding.UTF8.GetBytes(text);
var hash = SHA512.Create();
var aesKey = new byte[24];
Buffer.BlockCopy(hash.ComputeHash(Encoding.UTF8.GetBytes(key)), 0, aesKey, 0, 24);
using (var aes = Aes.Create())
{
if (aes == null)
throw new ArgumentException("Parameter must not be null.", nameof(aes));
aes.Key = aesKey;
using (var encryptor = aes.CreateEncryptor(aes.Key, aes.IV))
using (var resultStream = new MemoryStream())
{
using (var aesStream = new CryptoStream(resultStream, encryptor, CryptoStreamMode.Write))
using (var plainStream = new MemoryStream(buffer))
{
plainStream.CopyTo(aesStream);
}
var result = resultStream.ToArray();
var combined = new byte[aes.IV.Length + result.Length];
Array.ConstrainedCopy(aes.IV, 0, combined, 0, aes.IV.Length);
Array.ConstrainedCopy(result, 0, combined, aes.IV.Length, result.Length);
return Convert.ToBase64String(combined);
}
}
}
public static string Decrypt(this string encryptedText, string key)
{
if (string.IsNullOrEmpty(key))
throw new ArgumentException("Key must have valid value.", nameof(key));
if (string.IsNullOrEmpty(encryptedText))
throw new ArgumentException("The encrypted text must have valid value.", nameof(encryptedText));
var combined = Convert.FromBase64String(encryptedText);
var buffer = new byte[combined.Length];
var hash = new SHA512CryptoServiceProvider();
var aesKey = new byte[24];
Buffer.BlockCopy(hash.ComputeHash(Encoding.UTF8.GetBytes(key)), 0, aesKey, 0, 24);
using (var aes = Aes.Create())
{
if (aes == null)
throw new ArgumentException("Parameter must not be null.", nameof(aes));
aes.Key = aesKey;
var iv = new byte[aes.IV.Length];
var ciphertext = new byte[buffer.Length - iv.Length];
Array.ConstrainedCopy(combined, 0, iv, 0, iv.Length);
Array.ConstrainedCopy(combined, iv.Length, ciphertext, 0, ciphertext.Length);
aes.IV = iv;
using (var decryptor = aes.CreateDecryptor(aes.Key, aes.IV))
using (var resultStream = new MemoryStream())
{
using (var aesStream = new CryptoStream(resultStream, decryptor, CryptoStreamMode.Write))
using (var plainStream = new MemoryStream(ciphertext))
{
plainStream.CopyTo(aesStream);
}
return Encoding.UTF8.GetString(resultStream.ToArray());
}
}
}
public static string Encrypt(string text, byte[] key)
{
//string keyString = "encrypt123456789";
//var key = Encoding.UTF8.GetBytes(keyString);//16 bit or 32 bit key string
using (var aesAlg = Aes.Create())
{
using (var encryptor = aesAlg.CreateEncryptor(key, aesAlg.IV))
{
using (var msEncrypt = new MemoryStream())
{
using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
using (var swEncrypt = new StreamWriter(csEncrypt))
{
swEncrypt.Write(text);
}
var iv = aesAlg.IV;
var decryptedContent = msEncrypt.ToArray();
var result = new byte[iv.Length + decryptedContent.Length];
Buffer.BlockCopy(iv, 0, result, 0, iv.Length);
Buffer.BlockCopy(decryptedContent, 0, result, iv.Length, decryptedContent.Length);
return Convert.ToBase64String(result);
}
}
}
}
public static string Decrypt(string cipherText, byte[] key)
{
var fullCipher = Convert.FromBase64String(cipherText);
var iv = new byte[16];
var cipher = new byte[fullCipher.Length - iv.Length];//new byte[16];
Buffer.BlockCopy(fullCipher, 0, iv, 0, iv.Length);
Buffer.BlockCopy(fullCipher, iv.Length, cipher, 0, cipher.Length);
//var key = Encoding.UTF8.GetBytes(keyString);//same key string
using (var aesAlg = Aes.Create())
{
using (var decryptor = aesAlg.CreateDecryptor(key, iv))
{
string result;
using (var msDecrypt = new MemoryStream(cipher))
{
using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (var srDecrypt = new StreamReader(csDecrypt))
{
result = srDecrypt.ReadToEnd();
}
}
}
return result;
}
}
}
}
to activate encryption just add a tag
[JsonConverter(typeof(EncryptingJsonConverter))]

.NET 6 - Change Json Property Casing

How can I change the casing of the property names of a json without performing model binding?
JsonElement serialization ignores PropertyNaming JsonSerializer options as is also confirmed here: https://github.com/dotnet/runtime/issues/61843
The suggested use of JsonNode/JsonObject results in the same behavior.
Any hints how I can accomplish this?
As example I want to change this:
{
"MyPoperty" : 5,
"MyComplexProperty" : {
"MyOtherProperty": "value",
"MyThirdProperty": true
}
}
to this:
{
"myPoperty" : 5,
"myComplexProperty" : {
"myOtherProperty": "value",
"myThirdProperty": true
}
}
Cheers.
I think you try to use Newtonsoft json
class Person
{
public string UserName { get; set; }
public int Age { get; set; }
}
coding
static void Main(string[] args)
{
Person person = new Person();
person.UserName = "Bob";
person.Age = 20;
var serializerSettings = new JsonSerializerSettings();
serializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
var json = JsonConvert.SerializeObject(person, serializerSettings);
Console.WriteLine(json);
}
output
{"userName":"Bob","age":20}
not depend on Newtonsoft json but in the case of multi-layer objects
var json = #"{""ShouldWindUpAsCamelCase"":""does it?""}";
var obj = JsonSerializer.Deserialize<Dictionary<string,string>>(json);
var dic = new Dictionary<string, string>();
foreach (var item in obj)
{
dic.Add(item.Key.FirstCharToLower(), item.Value);
}
var serialized = System.Text.Json.JsonSerializer.Serialize(dic);
Console.WriteLine(serialized);
FirstCharToLower() function
public static string FirstCharToLower(this string input)
{
if (String.IsNullOrEmpty(input))
return input;
string str = input.First().ToString().ToLower() + input.Substring(1);
return str;
}
#output
{"shouldWindUpAsCamelCase":"does it?"}

Correct JSON structure using RestSharp

What is the Correct format for sending JSON using RestSharp:
Example PUT JSON:
{
"properties": [
{
"name": "description",
"value": "A far better description than before"
}
]
}
In C# how to correctly send, I'm attempting with:
request.AddJsonBody(new
{
properties = new[]
{
new{property="name",value="about_us"},
new{property="value",value="My description"}
}
});
Below is the full code:
private void UpdateCompanyProperty(string companyId)
{
var hapikey = "{YOUR_HAPI_KEY_HERE}";
var client = new RestClient("https://api.hubapi.com/");
var request = new RestRequest("companies/v2/companies/{companyId}", Method.PUT);
request.AddUrlSegment("companyId", companyId);
request.AddQueryParameter("hapikey", hapikey);
request.RequestFormat = DataFormat.Json;
request.AddJsonBody(new
{
properties = new[]
{
new{property="name",value="about_us"},
new{property="value",value="My description"}
}
});
IRestResponse response = client.Execute(request);
JObject jObject = JObject.Parse(response.Content);
JToken jvid = jObject["portalId"];
Debug.WriteLine(jvid);
}
No errors but not updating or returning values.
Try my answer here:
https://stackoverflow.com/a/57281157/5478655
request.RequestFormat = DataFormat.Json; // Important
var input = new Dictionary<string, object>();
// props could be an array or real objects too of course
var props = new[]
{
new{property="name",value="about_us"},
new{property="value",value="My description"}
};
input.Add("properties", props);
request.AddBody(input);
Create a class and give it any name
class MyClass
{
public string property {get;set;}
private string value {get;set;}
}
Define your class as an object
List<MyClass> list = new List<MyClass>
{
new MyClass() { property = "name", value = "about_us"},
new MyClass() { property = "value", value = "My Description"},
};
Now using Newtonsoft.Json serialize your object
string result = JsonConvert.SerializeObject(list);
Now add it to an array
var resArray = new object[] { result };
Find your modified code below
class MyClass
{
public string property {get;set;}
private string value {get;set;}
}
using Newtonsoft.Json;
using RestSharp;
private void UpdateCompanyProperty(string companyId)
{
List<MyClass> list = new List<MyClass>
{
new MyClass() { property = "name", value = "about_us"},
new MyClass() { property = "value", value = "My Description"},
};
string result = JsonConvert.SerializeObject(list);
var hapikey = "{YOUR_HAPI_KEY_HERE}";
var client = new RestClient("https://api.hubapi.com/");
var request = new RestRequest("companies/v2/companies/{companyId}", Method.PUT);
request.AddUrlSegment("companyId", companyId);
request.AddQueryParameter("hapikey", hapikey);
request.RequestFormat = DataFormat.Json;
request.AddJsonBody(new
{
properties =result
});
IRestResponse response = client.Execute(request);
JObject jObject = JObject.Parse(response.Content);
JToken jvid = jObject["portalId"];
Debug.WriteLine(jvid);
}

CsvHelper wrap all values with quotes

I am using CsvHelper I need to wrap all values with quotes.
Is that possible?
Data = is a List
using (StreamWriter textWriter = new StreamWriter(path))
{
textWriter.BaseStream.Write(p, 0, p.Length);
// var dt = new DataTable();
var csv = new CsvWriter(textWriter);
csv.WriteRecords(Data);
textWriter.Flush();
textWriter.Close();
}
Thanks
There is a config value called ShouldQuote where you can determine on a field level if it should be quoted.
void Main()
{
var records = new List<Foo>
{
new Foo { Id = 1, Name = "one" },
new Foo { Id = 2, Name = "two" },
};
using (var writer = new StringWriter())
using (var csv = new CsvWriter(writer))
{
csv.Configuration.ShouldQuote = (field, context) => true;
csv.WriteRecords(records);
writer.ToString().Dump();
}
}
public class Foo
{
public int Id { get; set; }
public string Name { get; set; }
}
Output:
"Id","Name"
"1","one"
"2","two"
From version 25.0.0 up to the date, the way of doing it is:
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
ShouldQuote = args => true
};
Just need to add a configuration object. like this
CsvHelper.Configuration.CsvConfiguration config = new CsvHelper.Configuration.CsvConfiguration();
config.QuoteAllFields = true;
var csv = new CsvWriter(textWriter, config);

Html.ListBoxFor Object reference not set to an instance of an object Error

I am using view model to display a dropdownlist and i am also trying to get the value of the selected list, here is my view model
public class CreateJobViewModel
{
public int[] SelectedIndustriesIds { get; set; }
public IList<SelectListItem> IndustriesList { get; set; }
}
My controller
public ActionResult Create()
{
var industryList = repository.GetAllIndustries();
var model = new CreateJobViewModel
{
IndustriesList = industryList.Select(i => new SelectListItem
{
Value = i.IndustryId.ToString(),
Text = i.Name
}).ToList()
};
return View("~/Views/Dashboard/Job/Create.cshtml", model);
}
My post controller
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(CreateJobViewModel model)
{
try
{
var job = new Job()
{
Title = "hi",
EmploymentHourId = 1,
LocationId = 1,
Salary = 50,
SalaryPeriodId = 1,
PostCode = 2131,
Role = "world",
Description = "hello",
IsPublished = false,
ShiftId = 1,
WorkDayId = 1,
NumberOfPosition = 5,
Meal = false,
SecondYearVisa = true,
Sponsorship = true,
Accommodation = true,
DurationId = 1,
IndustryExperiencePeriod = 5,
Id = User.Identity.GetUserId(),
};
foreach (int id in model.SelectedIndustriesIds)
{
var industry = repository.Industry(id);
job.Industries.Add(industry);
}
foreach (int id in model.SelectedSpecialRequirementsId)
{
var special = repository.SpecialRequirement(id);
job.SpecialRequirements.Add(special);
}
repository.AddJob(job);
return RedirectToAction("Create");
}
catch
{
return View("~/Views/Dashboard/Job/Create.cshtml");
}
}
Every time i try to submit the selected value, i get Object reference not set to an instance of an object Error on the following line in my view:
#model Taw.WebUI.Models.CreateJobViewModel
#Html.ListBoxFor(m => m.SelectedIndustriesIds, Model.IndustriesList) -- here i get the error
Any reason why?
When you submit the form your throwing an exception (confirmed in the comments) and in the catch block you are returning the view, which throws the exception you are seeing because Model.IndustriesList is null. You need to re-assign the value before you return the view.
Since you need to assign SelectLists in the GET method and in the POST method if you return the view, I tend to re-factor this to a separate method to keep the controller code a bit cleaner. Note the following code is based on your model property being public SelectList IndustriesList { get; set; } which is a bit simpler than building IList<SelectListItem>
private void ConfigureViewModel(CreateJobViewModel model)
{
var industryList = repository.GetAllIndustries();
model.IndustriesList = new SelectList(industryList, "IndustryId", "Name")
// any other common stuff
}
and then in the action methods
public ActionResult Create()
{
var model = new CreateJobViewModel();
ConfigureViewModel(model);
return View(model);
}
public ActionResult Create(CreateJobViewModel model)
{
try
{
....
}
catch
{
ConfigureViewModel(model);
return View(model);
}
}
Note its also good practice to test if the model is valid before attempting to save it
public ActionResult Create(CreateJobViewModel model)
{
if (!ModelState.IsValid)
{
ConfigureViewModel(model);
return View(model); // return the view so the user can correct validation errors
}
....