Deserializing simple string - c#

I'm sure I am missing something very obvious, and I've read different threads (like this one, this and also this, just to name the last ones) but I still cannot find the answer...
Here are my classes:
using System;
using Newtonsoft.Json;
namespace WebAPIClient
{
public class XWTournament
{
private string name;
[JsonProperty("name")]
public string Name { get => name; set => name = value; }
}
public class Root
{
public XWTournament xwtournam { get => xwtournam; set => xwtournam = value; }
}
}
And here I try to use them:
msg = "{\"tournament\": {\"Name\": \"Worlds 2014 Flight One\"}}";
Root root = JsonConvert.DeserializeObject<Root>(msg) ;
string pippo = root.xwtournam.Name;
But in this case I am receiving a stack overflow error...
What am I missing? How can I read the variables in the string?
Edit: thanks to the useful answers, I have corrected the code in this way
using System;
using Newtonsoft.Json;
namespace WebAPIClient
{
public class XWTournament
{
//I've deleted the private variable
public string Name { get; set; }
}
public class Root
{
[JsonProperty("tournament")]
public XWTournament xwtournam { get; set; }
}
}

None of your classes have a property named tournament. Your JSON does. What does that suggest?
public class Root
{
public XWTournament tournament { get; set; }
}
You also don't need the infinite recursion in the setter as you wrote it. Try assigning to it: The getter and the setter both just call themselves. That's the cause of the stack overflow exception. You'd get one if you tried to set that property, too.

Related

Does Getter and Setter in ASP.MVC can instantiate an object?

From Microsoft MVC doc, related to Authoring Tag Helpers, I can read this:
using System;
namespace AuthoringTagHelpers.Models
{
public class WebsiteContext
{
public Version Version { get; set; }
public int CopyrightYear { get; set; }
public bool Approved { get; set; }
public int TagsToShow { get; set; }
}
}
and this:
using System;
using AuthoringTagHelpers.Models;
using Microsoft.AspNetCore.Razor.TagHelpers;
namespace AuthoringTagHelpers.TagHelpers
{
public class WebsiteInformationTagHelper : TagHelper
{
public WebsiteContext Info { get; set; }
public override void Process(TagHelperContext context, TagHelperOutput output)
{
output.TagName = "section";
output.Content.SetHtmlContent(
$#"<ul><li><strong>Version:</strong> {Info.Version}</li>
<li><strong>Copyright Year:</strong> {Info.CopyrightYear}</li>
<li><strong>Approved:</strong> {Info.Approved}</li>
<li><strong>Number of tags to show:</strong> {Info.TagsToShow}</li></ul>");
output.TagMode = TagMode.StartTagAndEndTag;
}
}
}
I never saw this kind of code before, where public WebsiteContext Info { get; set; } can automagically instantiate an object???
How it works? Is there any documentation on it?
The answer is in the document you linked:
Note
In the Razor markup shown below:
<website-information info="new WebsiteContext {
Version = new Version(1, 3),
CopyrightYear = 1638,
Approved = true,
TagsToShow = 131 }" />
Razor knows the info attribute is a class, not a string, and you want to write C# code. Any non-string tag helper attribute should be written without the # character.
The tag helper itself doesn't know how to instantiate the instance. You have to do it manually in the Razor markup or set it to a default value in the property declaration or class constructor in order for it to be non-null. Here is an example of setting the instance in the property declaration.
public WebsiteContext { get; set; } = new WebSiteContext
{
Version = new Version(1, 3),
CopyrightYear = 1638,
Approved = true,
TagsToShow = 131
};
public WebsiteContext Info { get; set; } is not instantiating anything here. If you call the following code:
var websiteInformationTagHelper = new WebsiteInformationTagHelper();
then websiteInformationTagHelper.Info will be equal to null
Note, that it is now possible in c# to assign default values like the following which is a little bit different than what you are wondering about:
public WebsiteContext Info { get; set; } = new WebsiteContext()
Not automatically, but yes. The get and set keywords are shorthand for methods that are called after the property is accessed (get) or assigned to (set). You can add a body with a regular code block:
get { return _backingField; }
set { _backingField = value; }
The value keyword represents the value being assigned to the property and you can do most things in those blocks, same as any method, including instantiating an object.
Microsoft documentation - Auto implemented properties:
learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/auto-implemented-properties
If you're referring to instantiating the parent object, that I don't believe makes sense.

How to ignore a property based on a runtime condition?

I have a simple pair of classes which for I've set up a mapping at initialization time.
public class Order {
public int ID { get; set; }
public string Foo { get; set; }
}
public class OrderDTO {
public int ID { get; set; }
public string Foo { get; set; }
}
...
Mapper.CreateMap<Order, OrderDTO>();
Now at a certain point I need to map an Order to an OrderDTO. BUT depending on some circumstances, I might need to ignore Foo during mapping. Let's also assume that I cannot "store" the condition in the source or destination object.
I know how I can configure the ignored properties at initialization time, but I have no idea how I could achieve such a dynamic runtime behavior.
Any help would be appreciated.
UPDATE
My use case for this behaviour is something like this. I have an ASP.NET MVC web grid view which displays a list of OrderDTOs. The users can edit the cell values individually. The grid view sends the edited data back to the server like a collection of OrderDTOs, BUT only the edited field values are set, the others are left as default. It also sends data about which fields are edited for each primary key. Now from this special scenario I need to map these "half-empty" objects to Orders, but of course, skip those properties which were not edited for each object.
The other way would be to do the manual mapping, or use Reflection somehow, but I was just thinking about if I could use AutoMapper in some way.
I've digged into the AutoMapper source code and samples, and found that there is a way to pass runtime parameters at mapping time.
A quick example setup and usage looks like this.
public class Order {
public int ID { get; set; }
public string Foo { get; set; }
}
public class OrderDTO {
public int ID { get; set; }
public string Foo { get; set; }
}
...
Mapper.CreateMap<Order, OrderDTO>()
.ForMember(e => e.Foo, o => o.Condition((ResolutionContext c) => !c.Options.Items.ContainsKey("IWantToSkipFoo")));
...
var target = new Order();
target.ID = 2;
target.Foo = "This should not change";
var source = new OrderDTO();
source.ID = 10;
source.Foo = "This won't be mapped";
Mapper.Map(source, target, opts => { opts.Items["IWantToSkipFoo"] = true; });
Assert.AreEqual(target.ID, 10);
Assert.AreEqual(target.Foo, "This should not change");
In fact this looks quite "technical", but I still think there are quite many use cases when this is really helpful. If this logic is generalized according to application needs, and wrapped into some extension methods for example, then it could be much cleaner.
Expanding on BlackjacketMack's comment for others:
In your MappingProfile, add a ForAllMaps(...) call to your constructor.
using AutoMapper;
using System.Collections.Generic;
using System.Linq;
public class MappingProfile : Profile
{
public MappingProfile()
{
ForAllMaps((typeMap, mappingExpression) =>
{
mappingExpression.ForAllMembers(memberOptions =>
{
memberOptions.Condition((o1, o2, o3, o4, resolutionContext) =>
{
var name = memberOptions.DestinationMember.Name;
if (resolutionContext.Items.TryGetValue(MemberExclusionKey, out object exclusions))
{
if (((IEnumerable<string>)exclusions).Contains(name))
{
return false;
}
}
return true;
});
});
});
}
public static string MemberExclusionKey { get; } = "exclude";
}
Then, for ease of use, add the following class to create an extension method for yourself.
public static class IMappingOperationOptionsExtensions
{
public static void ExcludeMembers(this AutoMapper.IMappingOperationOptions options, params string[] members)
{
options.Items[MappingProfile.MemberExclusionKey] = members;
}
}
Finally, tie it all together: var target = mapper.Map<Order>(source, opts => opts.ExcludeMembers("Foo"));

SQLite doesn't know about class

I'm currently working on a Windows Forms application, and I would like to use a simple database which doesn't require a backing server or credentials to run, so I chose SQLite. But trying to get this thing to work so far was a nightmare.
Right now, I have a few, simple classes with simple properties, which I'd like to store in the database. I've appended the appropriate labels to everything ([Table("")] for classes, [PrimaryKey, AutoIncrement] for the Id property), but whenever I do Connection.CreateTable(CreateFlags.AutoIncPK) (it won't show the generic parameter, but it's there, I promise), it throws a NotSupportedException, saying "Don't know about MyProject.MyClass". I have also provided an empty, parameterless constructor in each class.
So, how do I make SQLite "know" about my class?
EDIT:
My Character.cs file:
[Table("Character")]
class Character
{
[PrimaryKey, AutoIncrement]
public int id { get; set; }
public string Name
{
get;
set;
}
public string FilePath
{
get;
set;
}
public Character()
{
}
public Character(string file)
{
this.FilePath = file;
this.Name = FetchName(file);
}
private string FetchName(string file)
{
string[] fileHolder = File.ReadAllLines("\\chars\\" + file);
foreach (string line in fileHolder)
{
if (line.ToLower().StartsWith("name"))
{
if (!line.Contains(';'))
return line.Split('=')[1].Trim().Trim('"');
else return line.Split('=')[1].Split(';')[0].Trim().Trim('"');
}
}
return string.Empty;
}
}
My Database.cs file:
class Database
{
private static SQLiteConnection Connection;
private static string ConnectionString = "MyProject.sqlite";
public Database()
{
if (!File.Exists("MyProject.sqlite"))
System.Data.SQLite.SQLiteConnection.CreateFile("MyProject.sqlite");
using (Connection = new SQLiteConnection(ConnectionString))
{
Connection.CreateTable<Character>(CreateFlags.AutoIncPK);
}
}
}
The class that describe your table has to be public.
In your example, the character class is not public.
You should change the declaration like this:
[Table("Character")]
public class Character
{
// your class code...
}
I know it's a bit late to answer the original question but since my Google-Fu led me here, I hope this will help others that encounter the same error message:
I started receiving this error message after I added as public static property to a class with a [table] attribute. After adding the [Ignore] attribute to the property, I no longer received the error message.

Set XML serialziation resulting doc root

I've got another problem (which might not be an issue in terms of coding problems) but more of principle..been bugging me for a while. I have this c# class, as follows:
namespace SMCProcessMonitor
{
public class Config
{
[XmlElement("Recipient")]
public string recipient;
[XmlElement("Server-port")]
public int serverport;
[XmlElement("Username")]
public string username;
[XmlElement("Password")]
public string password;
[XmlElement("Program")]
public List<Programs> mPrograms = new List<Programs>();
[Serializable]
[XmlRoot("Email-Config")]
public class Email
{
public string Recipient
{
get
{
return SMCProcessMonitor.ConfigManager.mConfigurations.recipient;
}
set
{
SMCProcessMonitor.ConfigManager.mConfigurations.recipient = value;
}
}
public int ServerPort
{
get
{
return SMCProcessMonitor.ConfigManager.mConfigurations.serverport;
}
set
{
SMCProcessMonitor.ConfigManager.mConfigurations.serverport = value;
}
}
public string Username
{
get
{
return SMCProcessMonitor.ConfigManager.mConfigurations.username;
}
set
{
SMCProcessMonitor.ConfigManager.mConfigurations.username = value;
}
}
public string Password { get; set; }
}
}
I can serialize this almost fine. (i recently changed simple get; set; to the full-works as seen above, but when serialising i get something like this;
<Config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Recipient>sd</Recipient>
<Server-port>1234</Server-port>
<Username>dk</Username>
<Password>kdkdk</Password>
</Config>
Basically I want to wrap these 4 tags in an "email-settings" tag.
Add the Serializable() and XmlRoot attributes up to the base class:
[Serializable()]
[XmlRoot("Email-Settings")]
public class Config
There are attributes to control aspects of xml serialization like this, see Controlling XML Serialization Using Attributes.
I think the one you want specifically is XmlRootAttribute.
You'll need to create an EmailSettings class that contains those 4 properties, and then make an instance of the EmailSettings class a member of your Config class.

Attributes of properties in MetadataType are ignored by EntLib Validation

It's an EntLib-Validator-issue again. I'm playing with EntLib 5.0 in C# and .Net 4.0 on XP pro.
I have some business objects (partial classes) generated by T4 templates. So I decided to put their validation attributes in buddy-classes by using MetadataTypeAttribute as definitely recommended by the documentation of entLib 5.0 (msdn).
But the Validator object I get from the ValidatorFactory doesn't know about the validation attributes, defined in the metadata-class.
The business object is defined like this:
[MetadataType(typeof(PatientMetadata))]
public partial class Patient
{
private string _Name;
private int _DiagnosisCount;
public int DiagnosisCount
{
get
{
return _DiagnosisCount;
}
set
{
if (value != _DiagnosisCount)
{
_DiagnosisCount = value;
}
}
}
public string Name
{
get
{
return _Name;
}
set
{
if (value != _Name)
{
_Name = value;
}
}
}
}
And the metadata class like this, according to documentation:
public class PatientMetadata
{
[RangeValidator(4)]
public int DiagnosisCount { get; set; }
[StringLengthValidator(64, ErrorMessage = "Name must not exceed 64 chars.")]
public string Name { get; set; }
}
If I know try to do validation this way:
var factory = ValidationFactory.DefaultCompositeValidatorFactory;
var validator = factory.CreateValidator<Patient>();
...then watching into validator (during debugging) already says, that it's just an AndCompositeValidator without any children validators.
Again, if I put the validation attributes right in the Patient class, it works perfectly.
By now, I have no real idea, what I'm missing here, since I think doing everything according to the docs.
Thanks in advance to you guys!
The property names of the metadata class must match the property names of the main class.
In your case your metadata class should look like:
public class PatientMetadata
{
[RangeValidator(0, RangeBoundaryType.Inclusive, 10, RangeBoundaryType.Ignore)]
public int DiagnosisCount { get; set; }
[StringLengthValidator(6, ErrorMessage = "Name must not exceed 6 chars.")]
public string Name { get; set; }
}
Also, the docs indicate the accepted approach is to declare all return types as object. However, the docs also talk about using properties but in their example use fields so take it under advisement. :)

Categories

Resources