I’ve created a custom rule by adding the
static partial void AddSharedRules()
{
RuleManager.AddShared<Tag>(
new CustomRule<String>(
"TagName",
"Invalid Tag Name, must be between 1 and 50 characters",
IsNullEmptyOrLarge));
}
to my Entity class.
I then added the rule (as seen on the video, although the video is dated and has wrong information):
public static bool IsNullEmptyOrLarge( string value )
{
return (value == null
|| String.IsNullOrEmpty(value)
|| value.Length > 50);
}
But now I have the calling code…
try
{
// some code
}
catch ( CodeSmith.Data.Rules… ??? )
{
// I can’t add the BrokenRuleException object. It’s not on the list.
}
I have: assign, security and Validation.
What’s the correct way to catch broken rule exceptions in PLINQO?
Here is what you need to do, first add a reference in your project to
System.ComponentModel.DataAnnotations
using CodeSmith.Data.Rules;
Then
try
{
context.SubmitChanges();
}
catch (BrokenRuleException ex)
{
foreach (BrokenRule rule in ex.BrokenRules)
{
Response.Write("<br/>" + rule.Message);
}
}
If you want to change the default message then you can go to your entity and change the attribute from
[Required]
to
[CodeSmith.Data.Audit.Audit]
private class Metadata
{
// Only Attributes in the class will be preserved.
public int NameId { get; set; }
[Required(ErrorMessage="please please please add a firstname!")]
public string FirstName { get; set; }
You can also use these types of data annotation attributes
[StringLength(10, ErrorMessage= "The name cannot exceed 10 characters long")]
[Range(10, 1000, ErrorMessage = "Value for {0} must be between {1} and {2}.")]
[RegularExpression(#"^[a-zA-Z''-'\s]{1,40}$", ErrorMessage = "Characters are not allowed.")]
public string FirstName { get; set; }
HTH
Related
Which JSON library would you use to parse JSON that contains beyond the usual text data, such as audioStream and binary objects buffers?
You may want to provide more information specific to your case.
The solution depends on whether you are:
Going to serialize that data yourself and then deserialize them.
You could use scriptable objects to store audio, sprite, prefab and in general visual-centric data. Then create an editor extension for that particular scriptable object type to expose and edit the JSON data and store them in a .json file in project's assets.
You already have everything serialized in JSON and just need a way to deserialize them.
In this case, you should probably create the Data class to hold those data, with the serializable types in mind. Then create those data and try to import the stream byte array to either an audio file in the file system or an audio clip in memory.
Here is an example that caches the file in a directory:
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using Newtonsoft.Json; // JSON .NET For Unity
using Newtonsoft.Json.Serialization;
[Serializable]
public class MovieModel
{
[Serializable]
public class SlotsData
{
public string Celebrity { get; set; }
public string Genre { get; set; }
}
[Serializable]
public class AudioStreamData
{
public string Type { get; set; }
public byte[] Data { get; set; }
}
public string UserId { get; set; }
public string ContentType { get; set; }
public string IntentName { get; set; }
public SlotsData Slots { get; set; }
public string Message { get; set; }
public string DialogState { get; set; }
public AudioStreamData AudioStream { get; set; }
public override string ToString()
{
return string.Format("[{0}, {1}, {2}, {3}]", UserId, ContentType, IntentName, Message);
}
}
public class MovieWithAudioClip
{
public MovieModel Model { get; set; }
public string CachedFileName { get; set; }
public AudioClip Clip { get; set; }
}
public class AudioClipJSONImporter : MonoBehaviour
{
private static readonly JsonSerializerSettings SERIALIZATION_SETTINGS = new JsonSerializerSettings()
{
// Read the docs to configure the settings based on your data classes and the JSON file itself.
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
private static readonly Dictionary<string, AudioType> WHITELISTED_CONTENT_TYPE_TO_UNITY_AUDIO_TYPE = new Dictionary<string, AudioType>()
{
// Append all the supported content types here with their corresponding type, so that Unity can read them.
{ "audio/mpeg", AudioType.MPEG}
};
private static readonly Dictionary<string, string> CONTENT_TYPE_TO_FILE_EXTENSION = new Dictionary<string, string>()
{
{ "audio/mpeg", ".mp3"}
};
[Header("Drag and drop a JSON movie entry here")]
[SerializeField]
private TextAsset m_MovieEntryJson;
[SerializeField]
private string m_ClipCacheDirectory = "Clips";
[Header("Drag and drop an Audio source here, to preview the current movie entry")]
[SerializeField] AudioSource m_AudioPreviewer;
// Click on the top right of the script when in edit mode to call this procedure.
[ContextMenu("Import JSON entry")]
private void ImportJsonEntry()
{
if (m_MovieEntryJson == null || string.IsNullOrEmpty(m_MovieEntryJson.text))
{
Debug.LogError("Drag and drop a JSON movie entry in the inspector.");
return;
}
MovieModel movieModel = JsonConvert.DeserializeObject<MovieModel>(m_MovieEntryJson.text, SERIALIZATION_SETTINGS);
Debug.LogFormat("Movie entry {0} imported.", movieModel);
Debug.Assert(movieModel != null, "Failed to load movie entry.");
Debug.AssertFormat(movieModel.AudioStream != null, "Failed to load audio stream for movie entry {0}", movieModel);
Debug.AssertFormat(movieModel.AudioStream.Data != null, "Failed to load audio stream byte array for movie entry {0}", movieModel);
if (movieModel == null || movieModel.AudioStream == null || movieModel.AudioStream.Data == null)
{
return;
}
string clipCacheDirName = Application.isPlaying ? Application.persistentDataPath : Application.streamingAssetsPath;
if (!string.IsNullOrEmpty(m_ClipCacheDirectory))
{
clipCacheDirName = Path.Combine(clipCacheDirName, m_ClipCacheDirectory);
}
AudioType supportedAudioType;
string fileExtension = null;
if (!WHITELISTED_CONTENT_TYPE_TO_UNITY_AUDIO_TYPE.TryGetValue(movieModel.ContentType, out supportedAudioType))
{
Debug.LogErrorFormat(
"Failed to load movie {0} with mime type: {1} as it is not in the mime type to extension whitelist.",
movieModel, movieModel.ContentType
);
return;
}
CONTENT_TYPE_TO_FILE_EXTENSION.TryGetValue(movieModel.ContentType, out fileExtension);
StartCoroutine(
GenerateAudioMovie(clipCacheDirName, fileExtension, supportedAudioType, movieModel, (MovieWithAudioClip movie) =>
{
if (m_AudioPreviewer != null)
{
m_AudioPreviewer.clip = movie.Clip;
m_AudioPreviewer.Play();
}
})
);
}
private IEnumerator GenerateAudioMovie(
string rootDirName,
string fileExtension,
AudioType audioType,
MovieModel movieModel,
Action<MovieWithAudioClip> onDone,
Action<string> onError = null
)
{
// Remove this is you can be sure the directory exists.
Directory.CreateDirectory(rootDirName);
// If you can create a non random ID based on the JSON data, that is better.
//
// Mainly, because you can check the file system in case it has already been downloaded and load the clip directly.
// Although, that makes sense only if you have a 'light' route to receive the movie data without the audio stream (which is now cached).
string cachedFileId = Guid.NewGuid().ToString();
string cachedFileName = Path.Combine(rootDirName, cachedFileId + fileExtension);
MovieWithAudioClip audioMovie = new MovieWithAudioClip()
{
Model = movieModel,
CachedFileName = cachedFileName
};
// Source: https://answers.unity.com/questions/686240/audioclip-oggmp3-loaded-from-byte-array.html
//
File.WriteAllBytes(cachedFileName, movieModel.AudioStream.Data);
Debug.LogFormat("Movie audio file exported at: {0}", cachedFileName);
WWW loader = new WWW(string.Format("file://{0}", cachedFileName));
yield return loader;
if (!System.String.IsNullOrEmpty(loader.error))
{
Debug.LogErrorFormat("Failed to load movie {0} at file {1} with error {2}.", movieModel, cachedFileName, loader.error);
if (onError != null)
onError(loader.error);
}
else
{
audioMovie.Clip = loader.GetAudioClip(false, false, audioType);
Debug.AssertFormat(audioMovie.Clip != null, "Failed to generate audio clip for movie entry {0}", movieModel);
if (audioMovie.Clip != null)
{
if (onDone != null)
onDone(audioMovie);
}
else
{
if (onError != null)
onError(loader.error);
}
}
}
}
The code above does not do any processing, it just tries to retrieve an audio clip out of the stream in the JSON and it fails for mp3. However, an audio file will be created in your file system and you can play it and make sure that the JSON parser works.
From there, you need to process the data based on what types you are going to support. Here is a relevant post. Check out the three steps of Kurt-Dekker's answer.
Now, it's up to you to handle the different audio types you want to support, platform compatibility .etc.
Good luck!
I know very little about JSON, and my searches have yielded nothing. Could you tell me a little more on what sort of data you are dealing with?
Try parsing the parts of the file you can with JSON and then for the parts you can't, try building your own parser.
Best of luck.
I have developed a custom validator Attribute class for checking Integer values in my model classes. But the problem is this class is not working. I have debugged my code but the breakpoint is not hit during debugging the code. Here is my code:
public class ValidateIntegerValueAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (value != null)
{
int output;
var isInteger = int.TryParse(value.ToString(), out output);
if (!isInteger)
{
return new ValidationResult("Must be a Integer number");
}
}
return ValidationResult.Success;
}
}
I have also an Filter class for model validation globally in application request pipeline. Here is my code:
public class MyModelValidatorFilter: IActionFilter
{
public void OnActionExecuting(ActionExecutingContext context)
{
if (context.ModelState.IsValid)
return;
var errors = new Dictionary<string, string[]>();
foreach (var err in actionContext.ModelState)
{
var itemErrors = new List<string>();
foreach (var error in err.Value.Errors){
itemErrors.Add(error.Exception.Message);
}
errors.Add(err.Key, itemErrors.ToArray());
}
actionContext.Result = new OkObjectResult(new MyResponse
{
Errors = errors
});
}
}
The model class with validation is below:
public class MyModelClass
{
[ValidateIntegerValue(ErrorMessage = "{0} must be a Integer Value")]
[Required(ErrorMessage = "{0} is required")]
public int Level { get; set; }
}
Can anyone please let me know why the attribute integer validation class is not working.
Model validation comes into play after the model is deserialized from the request. If the model contains integer field Level and you send value that could not be deserialized as integer (e.g. "abc"), then model will not be even deserialized. As result, validation attribute will also not be called - there is just no model for validation.
Taking this, there is no much sense in implementing such ValidateIntegerValueAttribute. Such validation is already performed by deserializer, JSON.Net in this case. You could verify this by checking model state in controller action. ModelState.IsValid will be set to false and ModelState errors bag will contain following error:
Newtonsoft.Json.JsonReaderException: Could not convert string to
integer: abc. Path 'Level', ...
One more thing to add: for correct work of Required validation attribute, you should make the underlying property nullable. Without this, the property will be left at its default value (0) after model deserializer. Model validation has no ability to distinguish between missed value and value equal to default one. So for correct work of Required attribute make the property nullable:
public class MyModelClass
{
[Required(ErrorMessage = "{0} is required")]
public int? Level { get; set; }
}
I have an object with predefined data structure:
public class A
{
public string Id {get;set;}
public bool? Enabled {get;set;}
public int? Age {get;set;}
}
and JSON is supposed to be
{ "Id": "123", "Enabled": true, "Age": 23 }
I want to handle JSON error in positive way, and whenever server returns unexpected values for defined data-types I want it to be ignore and default value is set (null).
Right now when JSON is partially invalid I'm getting JSON reader exception:
{ "Id": "123", "Enabled": "NotABoolValue", "Age": 23 }
And I don't get any object at all.
What I want is to get an object:
new A() { Id = "123", Enabled = null, Age = 23 }
and parsing warning if possible.
Is it possible to accomplish with JSON.NET?
To be able to handle deserialization errors, use the following code:
var a = JsonConvert.DeserializeObject<A>("-- JSON STRING --", new JsonSerializerSettings
{
Error = HandleDeserializationError
});
where HandleDeserializationError is the following method:
public void HandleDeserializationError(object sender, ErrorEventArgs errorArgs)
{
var currentError = errorArgs.ErrorContext.Error.Message;
errorArgs.ErrorContext.Handled = true;
}
The HandleDeserializationError will be called as many times as there are errors in the json string. The properties that are causing the error will not be initialized.
Same thing as Ilija's solution, but a oneliner for the lazy/on a rush (credit goes to him)
var settings = new JsonSerializerSettings { Error = (se, ev) => { ev.ErrorContext.Handled = true; } };
JsonConvert.DeserializeObject<YourType>(yourJsonStringVariable, settings);
Props to Jam for making it even shorter =)
There is another way. for example, if you are using a nuget package which uses newton json and does deseralization and seralization for you. You may have this problem if the package is not handling errors. then you cant use the solution above. you need to handle in object level. here becomes OnErrorAttribute useful. So below code will catch any error for any property, you can even modify within the OnError function and assign default values
public class PersonError
{
private List<string> _roles;
public string Name { get; set; }
public int Age { get; set; }
public List<string> Roles
{
get
{
if (_roles == null)
{
throw new Exception("Roles not loaded!");
}
return _roles;
}
set { _roles = value; }
}
public string Title { get; set; }
[OnError]
internal void OnError(StreamingContext context, ErrorContext errorContext)
{
errorContext.Handled = true;
}
}
see https://www.newtonsoft.com/json/help/html/SerializationErrorHandling.htm
I use a DataContractJsonSerializer to create a JsonResult for my model data when sending data to the client. My model represents data to be displayed in a data table, and I wished to change the name of the model's properties in the JSON only so that less verbose property names are sent over the wire for each data table row. Now, I'm attempting to send the data table cell values via JSON to the server's controller action method. The names of the fields being sent back are still the short names, and the model binding doesn't seem to like that. What can I do to get model binding working and preserve the ability to sent alternate property names via JSON?
Model:
[DataContract()]
public class UsageListModel {
[DataMember(Name = "results")]
public IEnumerable<UsageModel> Usages { get; set; }
}
[DataContract()]
public class UsageModel {
[DataMember(Name = "job")]
public string JobId { get; set; }
[DataMember(Name = "dt")]
public DateTime UsageDate { get; set; }
[DataMember(Name = "qty")]
public int Quantity { get; set; }
[DataMember(Name = "uom")]
public string UnitOfMeasure { get; set; }
[DataMember(Name = "nts")]
public string Notes { get; set; }
}
It's not as elegant but I usually do this by just making an intermediary class (I refer to it as a ViewModel) that has those shortname properties and can be translated back and forth between it and the actual Model. Although it seems like busy work, the ViewModel can be useful beyond this stint - for example you can use it to easily cache client-side info if the need arises, or serialize/deserialize exactly what's going to/from the client in tests.
I'm still in disbelief that MVC doesn't offer some easier method to bind using custom attributes (or even the .NET data-contract attributes). Given that it doesn't... your best bet is to implement your own IModelBinder. Use reflection to get the DataMember names of the properties, and look for those values in the binding context.
Here's a great reference on model binding: http://msdn.microsoft.com/en-us/magazine/hh781022.aspx
A good general approach to maintaining custom binders: http://lostechies.com/jimmybogard/2009/03/18/a-better-model-binder/
EDIT
Generic model binder that handles a defined type. To add this to your application, add this line in global.asax:
ModelBinders.Binders.Add(typeof(UsageModel), new CustomModelBinder<UsageModel>());
And the binder:
public class CustomModelBinder<T> : IModelBinder
{
public override bool IsMatch(Type t)
{
return t == typeof(T);
}
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
Type t = typeof(T);
var entity = (bindingContext.Model ?? Activator.CreateInstance(t));
// Cycle through the properties and assign values.
foreach (PropertyInfo p in t.GetProperties())
{
string sourceKey;
// this is what you'd do if you wanted to bind to the property name
// string sourceKey = p.Name;
// TODO bind sourceKey to the name in attribute DataMember
Type propertyType = p.PropertyType;
// now try to get the value from the context ...
ValueProviderResult valueResult = bindingContext.ValueProvider.GetValue(sourceKey);
if (valueResult != null)
{
bindingContext.ModelState.SetModelValue(sourceKey, valueResult);
p.SetValue(entity, valueResult.ConvertTo(propertyType), null);
}
}
return entity;
}
}
I stumbled across a potential answer to this question randomly while browsing this other question.
I never realized this until now, but apparently you can add attributes to method parameters. Let's take a simple example:
public ActionResult SomeMethod(string val) {
return View(val);
}
If you call this URL -- /MyController/SomeMethod?val=mytestval -- then you'll get back "mytestval" in the model, right? So now you can write this:
public ActionResult SomeMethod([Bind(Prefix="alias")] string val) {
return View(val);
}
Now this URL will produce the same result: /MyController/SomeMethod?alias=mytestval.
Anyway, I'm still not sure if that will answer your question, but I thought it was very interesting.
I am following the Summer of NHibernate tutorials but I am not using the xml mappings but instead, I am making use of Fluent NHibernate to do the mappings.
My Customer entity class is:
public class Customer
{
public virtual int CustomerId { get; set; }
public virtual string Firstname { get; set; }
public virtual string Lastname { get; set; }
}
The corresponding mapping class is:
public class CustomerMap: ClassMap<Customer>
{
public CustomerMap()
{
Id(x =>x.CustomerId);
Map(x => x.Firstname).Length(50).Nullable();
Map(x => x.Lastname).Length(50).Nullable();
ImportType<CustomerFirstnameCounter>();
}
}
My DAO class is:
public int AddCustomer( Customer customer )
{
using( ISession session = GetSession() )
{
using( ITransaction tx = session.BeginTransaction() )
{
try
{
int newId = ( int ) session.Save( customer );
session.Flush();
tx.Commit();
return newId;
}
catch( GenericADOException )
{
tx.Rollback();
throw;
}
}
}
}
And finally my test is:
[Test]
public void AddCustomerThrowsExceptionOnFail()
{
// Arrange
Customer customer = BuildInvalidCustomer();
// Act
_provider.AddCustomer( customer );
// Assert
}
When the test runs, no exception is thrown! So my first question is whether anyone can see what is wrong with my mapping.
Now, in the dB, the Firstname field is set as a varchar(50). When I debug the test, I see that the data is inserted but truncated (I do get warning messages). So this might indicate
that I haven't set the dB up properly. Can anyone point me in the direction of where to prevent this truncation of data in SQL Server?
This answer should help you.
I will also use Data Annotation StringLengthAttribute to ensure validation of your properties
.Length(50) does not check lengths at run-time. This is only used if you are generating the database schema from the mappings.
If you wish to validate the length of values you will have to either do this manually or use some validation framework like NHibernate Validator