JSON to C# object using Data Contracts - What am I missing here? - c#

I am experiencing an error converting JSON to a strongly-typed class.
My JSON: {"listBoxID":"ctl00_ctl00_MainContentRoot_MainContent_lstBxSettings","sourceItemText":"Horizontal Bar","sourceItemValue":"Horizontal"}
DroppedItem droppedItem = JsonConvert.DeserializeObject<DroppedItem>(json);
/// <summary>
/// Outlines an object which is useful in simplifying how a CormantRadDock is created.
/// Instead of passing in lots of parameters, would rather just pass in an object that the
/// CormantRadDock knows how to interpret.
/// </summary>
[DataContract]
public class DroppedItem
{
private static readonly ILog Logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
[DataMember(Name = "sourceItemText")]
public string Text { get; set; }
[DataMember(Name = "sourceItemValue")]
public string Value { get; set; }
[DataMember(Name = "listBoxID")]
public Reports ReportType { get; set; }
public DroppedItem() { }
public DroppedItem(string text, string value, string listBoxID)
{
Logger.DebugFormat("Text: {0}, Value: {1}, slidingPaneTitle: {2}", text, value, listBoxID);
Text = text;
Value = value;
ReportType = DetermineReportType(listBoxID);
}
private Reports DetermineReportType(string listBoxID)
{
if (listBoxID.Contains("lstBxHistorical"))
{
return Reports.HistoricalReport;
}
else if (listBoxID.Contains("lstBxCustom"))
{
return Reports.CustomReport;
}
else
{
return Reports.None;
}
}
}
The issue is with converting listBoxID to ReportType.
Uncaught Sys.WebForms.PageRequestManagerServerErrorException: Sys.WebForms.PageRequestManagerServerErrorException: Error converting value "ctl00_ctl00_MainContentRoot_MainContent_lstBxSettings" to type 'CableSolve.Web.Reports'
It occurs regardless of whether the if statement finds a hit or defaults to the else block. It does not occur if I do not attempt to pass the listBoxID parameter.
I'm missing something here. Are my DataMember names not doing anything? I thought they would map the listBoxID to the correct property.

Change to something like this:
public Reports ReportType { get; set; }
[DataMember(Name = "listBoxID")]
public string listBoxID
{
set
{
ReportType = DetermineReportType(value);
}
}
Because basically, you can convert that string to a Report without your helper method. The constructor is not being called on deserialization

Related

Initializing property value in one class using the return value of a method in a different class

I have the following structure:
public class LogicStatement : ILogicStatement
{
public string TestLogic { get; set; }
public string CompareLogic { get; set; }
public string Operator { get; set; }
public string Expression();
public bool Value();
}
public class Test : ITest
{
public int TestId { get; set; }
public int LiteralId { get; set; }
public string TestName { get; set; }
public string TestText { get; set; }
public string TestDisplayName { get; }
**public ILogicStatement LogicStatement { get; set; }**
public string Expression { get; set; }
public bool Value { get; set; }
}
public class Literal : ILiteral
{
some property members...
**public List<ITest> Tests {get; set;}**
some method members...
}
Note that the class Test has a member of type LogicStatement, and the class Literal has a member of type List.
Note also that all classes have properties and methods that share the same name: Expression, Value, Expression(), Value().
The value of Expression and Value (properties and methods) depend on values in the LogicStatement class.
Throughout the whole project, I use the Interface Type for to instantiate each object to adhere with Dependency Inversion. To support this, I use a factory-like design to create new instances of Test and LogicStatement.
Example:
public static class Factory
{
public static ILogicStatement CreateLogicStatement()
{
return new LogicStatement();
}
public static ITest CreateTest()
{
return new Test(CreateLogicStatement());
}
public static List<ITest> CreateTests()
{
return new List<ITest>();
}
//repeat the same for evey other class.
}
My goal is to have Expression() and Value() be calculated only once in the bottom level class (LogicStatement), and somehow get transfered to their counterpart properties in the higher level classes.
I'm getting the data from Dapper and it looks like all the nested objects are returned from the Dapper module correctly with the same nested structure I intended, and with the right values for all of their members. All of them but Expression, Expression(), Value, Value() are null.
my constructors look like this:
public LogicStatement()
{
Expression();
Value();
}
public Test(ILogicStatement logicStatement)
{
_logicStatement = logicStatement;
Expression = _logicStatement.Expression();
Value = _logicStatement.Value();
}
public Literal(ITest test)
{
_test = test;
Expression = _test.Expression;
Value = _test.Value;
}
and my main:
List<ILiteral> literals = Factory.CreateLiterals();
List<ITest> tests = Facotry.CreateTests();
List<ILogicStatement> logicStatements = Factory.CreateLogicStatements();
literals = GetDataFromDapper();
This last line seems to assign correct values to all other members on all hierarchies. But I cannot get Expression and Value to be anything other than null.
If I test LogicStatement.Expression() and LogicStatement.Value() standalone, they do return the expexted values. but starting at the first parent class Test, these properties are all null.
I think I'm doing something wrong in the way i'm instantiating my objects. Primarily because I'm not sure i understand basic best practices to write constructors.
Maybe I the desired behavior should be implemented through events, where the Test and Literal classes subscribe to changes in the Expression() and Value() methods (or rather to what calculates them). But I never used events and I'd like to know if this fundamentally can be acheived without them first.
My question: How do I make the Expression() Value() at the bottom level class "Fire up" whenever LogicStatement is instantiated, and then have the Expression and Value properties be assigned accordingly as a result.
In other words, I want the following to always be true:
test[i].Expression == literal[i].Expression == LogicStatement[i].Expression()
I'm a beginner in OOP. So any fundamental explanation is welcome.
As you are new to object oriented programming I would start with the basics and leave factories and adhering with Dependency Inversion and the interfaces away for later.
You could tell Dapper to split joined tables into multiple entities (see https://www.learndapper.com/relationships), but for learning OOP I would start doing everything manually.
Your class design does not look proper to me yet. Not sure what Expression and Value of the LogicStatement are, but if they are calculations based on the other properties, I would implement them as (just to show off with complicated words) lazy initialized cached getter properties that are invalidated in the setters of the relevant properties. That ensures you only calculate them once for as many reads you like but recalculate them on first read after one or multiple properties have been updated.
public class LogicStatement {
private string _testLogic;
private string _compareLogic;
private string _operator;
private string? _expression;
private bool? _value;
public LogicStatement(string testLogic, string compareLogic, string #operator) {
_testLogic = testLogic;
_compareLogic = compareLogic;
_operator = #operator;
}
public string TestLogic {
get {
return _testLogic;
}
set {
_testLogic = value;
InvalidateCachedValues();
}
}
public string CompareLogic {
get {
return _compareLogic;
}
set {
_compareLogic = value;
InvalidateCachedValues();
}
}
public string Operator {
get {
return _operator;
}
set {
_operator = value;
InvalidateCachedValues();
}
}
public string Expression {
get {
string? result = _expression;
if (result is null) {
_expression = result = BuildExpression();
}
return result;
}
}
public bool Value {
get {
bool? result = _value;
if (result is null) {
_value = result = EvaluateValue();
}
return result.Value;
}
}
private void InvalidateCachedValues() {
_expression = null;
_value = null;
}
private string BuildExpression() {
//Your logic goes here
throw new NotImplementedException();
}
private bool EvaluateValue() {
//Your logic goes here
throw new NotImplementedException();
}
}
Sorry, it got a bit bigger with the full properties.
In the other classes I would not copy the Value and the Expression but simply remove these properties as anybody can easily access them through the LogicStatement property:
public class Test {
public Test(int testId, int literalId, string testName, string testText, string testDisplayName, LogicStatement logicStatement) {
TestId = testId;
LiteralId = literalId;
TestText = testText;
TestDisplayName = testDisplayName;
LogicStatement = logicStatement;
}
public int TestId { get; }
public int LiteralId { get; }
public string TestName { get; }
public string TestText { get; }
public string TestDisplayName { get; }
public LogicStatement LogicStatement { get; }
}
and the Literal could look like this (I got a bit confused whether this class has one Test or a list of them, I stick to your constructor + properties that hint in the direction of a single one):
public class Literal {
private Test _test;
public Literal(string property1, int property2, Test test) {
Property1 = property1;
Property2 = property2;
_test = test;
}
public string Property1 { get; }
public int Property2 { get; }
public string Expression => _test.LogicStatement.Expression;
public bool Value => _test.LogicStatement.Value;
}
As you decided not to expose the Test in the Literal it makes sense to provide Expression and Value, otherwise they could also be removed (or kept for convenience).

DataContractJsonSerializer not working

I use the DataContractJsonSerializer to convert a JSON string into a class, but always return an empty object.
I tested the string with the' JSON Viewer' extension in Notepad, is valid. Searched for a bug for a long time and compared other examples.
This is my JSON string in shortened form:
{
"error":[],
"result": {
"BCH": {"aclass":"currency","altname":"BCH","decimals":10,"display_decimals":5},
"DASH": {"aclass":"currency","altname":"test"}
}
}
The classes GetAssetInfoResponse and AssetInfo contain properties with DataMember attributes, but the property Result (after Deserialize) does not contain any objects.
[DataContract]
[KnownType(typeof(AssetInfo))]
public class GetAssetInfoResponse
{
[DataMember(Name = "error")]
public List<string> Error { get; set; }
[DataMember(Name = "result")]
public List<Dictionary<string, AssetInfo>> Result { get; set; }
}
[DataContract]
public class AssetInfo
{
/// <summary>
/// Alternate name.
/// </summary>
[DataMember(Name = "altname")]
public string Altname { get; set; }
/// <summary>
/// Asset class.
/// </summary>
[DataMember(Name = "aclass")]
public string Aclass { get; set; }
/// <summary>
/// Scaling decimal places for record keeping.
/// </summary>
[DataMember(Name = "decimals")]
public int Decimals { get; set; }
/// <summary>
/// Scaling decimal places for output display.
/// </summary>
[DataMember(Name = "display_decimals")]
public int DisplayDecimals { get; set; }
}
This is my test code:
var stream = new MemoryStream(Encoding.Unicode.GetBytes(strName))
{
Position = 0
};
var serializer = new DataContractJsonSerializer(typeof(GetAssetInfoResponse));
GetAssetInfoResponse test = (GetAssetInfoResponse)serializer.ReadObject(stream);
Console.ReadLine();
I can't use the Newtonsoft.Json extension because the project should not contain any external dependencies.
Is there another way to transfer JSON strings into classes?
Thank you for your time
You declare Result as a List<Dictionary<string, AssetInfo>> but from the format it looks like a dictionary, not a list of dictionaries (because it starts with {, this is used for objects, or dictionaries, not [ which is used for arrays/lists) . To use this format for dictionaries, you need to configure the UseSimpleDictionaryFormat property
var serializer = new DataContractJsonSerializer(typeof(GetAssetInfoResponse), new DataContractJsonSerializerSettings
{
UseSimpleDictionaryFormat = true
});
With this setting and this change, it worked:
public Dictionary<string, AssetInfo> Result { get; set; }

How to get properties from an object without initializing or constructing the object

It don't sound like something that should be possible, but I'd like to ask prior to writing it off.
I am writing an app with a plugin system, I would like to get the properties contained inside the plugin class prior to initializing or constructing it
Interface:
public interface IMPlugin
{
string PluginName { get; }
string PluginSafeName { get; }
string PluginDescription { get; }
Version PluginVersion { get; }
string PluginAuthorName { get; }
[DefaultValue(null)]
string PluginAuthorEmail { get; }
[DefaultValue(null)]
string PluginAuthorURL { get; }
[DefaultValue(false)]
bool PluginActive { get; set; }
/// <summary>
/// Plugin ID issued by InputMapper. Used for updates. </summary>
int PluginID { get; }
}
Plugin:
public class InputMonitor : IMPlugin,ApplicationPlugin
{
public string PluginName { get { return "My Plugin"; } }
public string PluginSafeName { get { return "MyPlugin"; } }
public string PluginDescription { get { return "My Plugin."; } }
public Version PluginVersion { get { return new Version("0.1.0.0"); } }
public string PluginAuthorName { get { return "John Doe"; } }
public string PluginAuthorEmail { get { return "foo#bar.com"; } }
public string PluginAuthorURL { get { return ""; } }
public int PluginID { get { return 0; } }
public bool PluginActive { get; set; }
public InputMonitor()
{
}
}
My issue is I want my application to be able to retrieve the plugin info prior to initializing it so users can activate and deactivate plugins at will but the details of the plugin still be visible in a plugin browser. But I don't want to have to rely on the plugin developers to not do anything in their constructor and only put their code in other interfaced methods.
Can a object be initialized and forcibly have its constructor ignored, or is there something to disallow constructors all together?
It sounds like your plugins should be configured via attributes rather than via exposing properties:
[Plugin(Name = "My Plugin", SafeName = "MyPlugin" ...)]
public class InputMonitor
You can access the attributes via reflection without creating an instance of the class.
After all, this is metadata about the type - not information about any one instance of the type.
Initializing a class and calling its constructor is the same thing.
Either you construct an instance of your plugin class just to read its properties and throw it away afterwards.
Or
You have a second helper/describer class. (which I understood you don't want)

servicestack.text deserializing array to object

I have a REST-Service build with ServicStack and in one call the user can send different types of values. So I made the property in C# of type object.
The JSON that is sent looks like this:
{"name":"ffff","params":[{"pId":1,"value":[624,625]},{"pId":2,"value":"xxx"}]}
The part "value":[624,625] results in a string object filled with "[624,625]". I was hoping to get an int-array or at least a string array, but it is plain string.
I set JsConfig.TryToParsePrimitiveTypeValues = true, but that doesn't seem to have any effect.
I tried the latest sources from github.
Can this be done with any combination of switches or must I parse this myself?
Thanks
EDIT:
Here is some testcode:
[TestMethod]
public void JsonTest()
{
string json = "{\"name\":\"ffff\",\"params\":[{\"pId\":1,\"value\":[624,625]},{\"pId\":2,\"value\":\"xxx\"}]}";
var x = JsonSerializer.DeserializeFromString<xy>(json);
Assert.AreEqual(x.Params[0].Value.GetType(), typeof(int[]));
}
public class xy
{
public string Name { get; set; }
public List<Param> Params { get; set; }
}
public class Param
{
public int PId { get; set; }
public object Value { get; set; }
}
If you change the type of "Value" to int array as follows, then ServiceStack will serialize to array of int.
public class Param
{
public int PId { get; set; }
public int[] Value { get; set; }
}
The following unit test passes:
[TestMethod]
public void JsonTest()
{
string json = "{\"name\":\"ffff\",\"params\":[{\"pId\":1,\"value\":[624,625]},{\"pId\":2,\"value\":\"xxx\"}]}";
var x = JsonSerializer.DeserializeFromString<xy>(json);
Assert.AreEqual(x.Params[0].Value.GetType(), typeof(int[]));
// Show that we have some integers
Assert.IsTrue(x.Params[0].Value.Count()>0);
}
If you cannot change the type of Value for any reason, then you can use ServiceStack.Text to serialize the string into an array as needed.

Using an interface as an out parameter in WCF web service

My situation is this. On every call to my web service, I have an out parameter which is an error object. If there is no error, the object indicates so by being empty. If there is an error then different properties are populated such as a "HasError" field, "ErrorMessage", "PrettyMessage", etc. What I'm trying to do with this now is create different types of Error object which all implement an Error interface which I've defined already. I then want to be able to have the out parameter be something like "out IMyError error" and then be able to set that error object either to one of my implementations of that interface depending on what kind of error I get in my method. The problem I'm getting is that serialization doesn't seem to like this. The method runs fine, but I don't get any data back on the client end. Here is some code to hopefully clarify.
My interface
public interface IMyError
{
bool HasError { get; set; }
string ErrorType { get; set; }
string PrettyErrMsg { get; set; }
}
An example class implementation
[Serializable]
[DataContract]
public class AspError : IMyError
{
public AspError(Exception exception)
{
this.HasError = true;
this.PrettyErrMsg = "An ASP Exception was thrown";
this.ExceptionMsg = exception.Message;
this.StackTrace = exception.StackTrace;
}
[DataMember(Name = "has_error", IsRequired = true)]
public bool HasError { get; set; }
[DataMember(Name = "error_type", IsRequired = true)]
public string ErrorType
{
get
{
return "ASP";
}
set
{
}
}
[DataMember(Name = "pretty_error", IsRequired = true)]
public string PrettyErrMsg { get; set; }
[DataMember(Name = "exception", IsRequired = true)]
public string ExceptionMsg { get; set; }
[DataMember(Name = "stack_trace", IsRequired = true)]
public string StackTrace { get; set; }
}
And the method in my WCF service
public bool MyMethod(out IMyError error)
{
error = new MyError() { HasError = false };
try
{
// do some code
}
catch (Exception exception)
{
error = new AspError(exception);
return false;
}
}
What I want is for that method, when an exception is caught, to return a AspError formatted as json the way it has worked in the past before I tried doing it as an interface. Or if an different implemented class of IMyError occurs then return THAT type formatted as json. I assumed it could work since they are both IMyError classes.
You need to send hint to client how to deserialize your interface. WCF does that with KnownTypeAttribute.
Your service should be changed to look like this:
[KnownType(typeof(AspError))]
public bool MyMethod(out IMyError error)
{
...
}
You can find more details in MSDN - http://msdn.microsoft.com/en-us/library/ms730167.aspx
And of course your client should have access to assembly with type AspError to be able to construct that type.

Categories