I am trying to deserialize an attribute with a list of pages to a List<String> object. If I use a space delimiter, I am able to deserialize it just fine, but I want it to look cleaner with a comma or a vertical bar.
Is it possible to deserialize
<Node Pages="1,2">
into a List<String> or List<int>?
My class is simple as well.
public List<int> Pages;
XmlSerializer does not support this out of the box for attributes. If you are in control of the structure of the XML, consider using child elements because using character separated attribute strings kind of beats the purpose of XML altogether.
You could work around it though using a computed property. But bear in mind that this will be slower because every time you access this property, you will parse the comma-separated string.
[XmlAttribute(AttributeName = "Pages")]
public string PagesString { get; set; }
public IList<int> Pages {
get {
return PagesString.Split(',').Select(x => Convert.ToInt32(x.Trim())).ToList();
}
set {
PagesString = String.Join(",", value);
}
}
Also, this silly implementation does not take into account that there might be wrong input in your string. You should guard against that too.
Another thing to notice is that every time you access the Pages property, a new collection is returned. If you invoke Add() on the returned value, this change will not be reflected in your XML.
Related
This is killing me because I feel like I've seen something around before, but my searches are coming up empty and I don't have various patterns committed to memory (yet).
The Situation
I have a search grid that allows users to filter based on multiple attributes. The filters that users would like are passed in via a NameValueCollection and parsed out.
Currently, our legacy app checks for each potential filter in a separate if statement and then runs custom code. And since cyclomatic complexity just isn't my thing, 30 almost identical if statements bug me.
Towards a Better Way
Rather than continuing to check each item in a hard-coded way, I've re-written the parser so that now I can quickly obtain a list of all filters that will be applied. I'm also refactoring the filters themselves into their own classes.
So, I have a list of filters in string form ("Name", "BirthDate", etc.) and a bunch of classes (NameFilter, BirthDateFilter, etc.)
The Question
What is a good, standard way to take a list of strings representing filter names and turn it into a list of those filter objects themselves?
I suppose I could do some sort of FilterLocator with a LocateFilter(string filterName) method, but that seems like it would lead to one long switch statement and seems kind of blech.
I get the sense this is heading towards some sort of IoC/DI solution, but that I don't know enough about either to fully realize it yet.
Thanks for your thoughts!
Use a dictionary where the key is your filter name, and the value is your filter object.
Rather than hard coding this dictionary, create a custom attribute that gives each filter object a filter name. Then, use reflection to find those objects and create a static dictionary that can be used later. This way, there is no hard coding of the individual filter mappings at all.
Here's a sample set up for this:
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class FilterNameAttribute : Attribute
{
public FilterNameAttribute(string filterName)
{
FilterName = filterName;
}
public string FilterName { get; private set; }
}
[FilterName("MyFilterName")]
public class MyFilter
{
//Do whatever you want here
}
public static class FilterHelper
{
static Dictionary<string, Type> _filterTypesDict;
static FilterHelper()
{
var assembly = Assembly.GetExecutingAssembly();
_filterTypesDict = assembly.GetTypes()
.Select(type => new { type, attrib = (FilterNameAttribute)type.GetCustomAttributes(typeof(FilterNameAttribute), false).FirstOrDefault() })
.Where(x => x.attrib != null)
.ToDictionary(x => x.attrib.FilterName, x => x.type);
}
public static Type GetFilterType(string filterName)
{
Type filterType;
if (!_filterTypesDict.TryGetValue(filterName, out filterType))
{
throw new Exception("Unknown Filter Name.");
}
return filterType;
}
public static object GetFilter(string filterName)
{
return Activator.CreateInstance(GetFilterType(filterName));
}
}
Like in the comments I would definitely create a dictionary for your filters. Also with that I would create a base class for each filter with Name property and Apply method. And when your application starts up you can gather filter classes with reflection by the base filter class and populate the dictionary of filters using Name property as the key (or you can just use the name of the filter class), this way you don't even need to build up the dictionary manually, every time you add a filter class it will automatically appear int the dictionary. This will allow your LocateFilter(string filterName) method be really simple and straightforward.
I have some code that basically checks the list of queues a current business object has been through. These queues are kept in an array aptly named _queueNames of type IKeyMap, a custom object my company uses.
I would like to get the textual names of the queues, as I need to check for the presence of a particular keyword and handle it separately if it's hit that particular queue.
I was hoping I could just do something like this;
var queues = _queueNames.ToArray().ToString();
if (queues.Contains("Condition"))
DoSomethingElse();
but that just gives me the object type, rather than a collection of the values. Looking at the KeyMap object, looks like just a simple key/value pair, might there be another way to do this?
Edit: KeyMap class & interface:
public interface IKeyMap : IDisposable
{
string Ley {get;}
string Field {get;}
}
public class KeyMap : IKeyMap
{
string _key, field;
public KeyMap(string key, string field)
{
_key = key;
_field = field;
}
public override string ToString()
{
return string.Format("{0}_{1}", Key, Field);
}
public string Key { get {return _key; } }
public string Field { get {return _field; } }
}
I left out some overrides, such as hashing & the Dispose method since I've got to manually type this out, can't copy-paste from my remote session :(
Without knowing what the objects inside of _queueNames look like, there is no exact answer. One mistake being made here is that you are checking a single string representing an entire array. What you want to do is check every object in the array for some value, or convert it to a string and check that value.
Here is an example:
foreach (var item in array)
{
if (item.ToString().Contains("Condition"))
{
DoSomethingElse();
break;
}
}
Or the LINQ way:
if (array.Any(item => item.ToString().Contains("Condition")))
DoSomethingElse();
This specific example only works if the object can be converted into a string that is useful to parse. You could also be accessing a member or invoking a function on said object to get your string. We can't know without more information, but hopefully this points you in the right direction.
In your IKeyMap interface, let's add a Boolean.
public string IsSpecial { get; set; }
When you create the object, set the IsSpecial flag. Then read it later..
var queues = _queueNames.ToArray().ToString();
if (queues.IsSpecial)
DoSomethingElse();
This avoids searching for strings, which is something you want to avoid. What if one of the other queues accidently end up with that string? Or what if you change the special string in one place but forget to change it in another? Or what if the capitalization is different? Or what if the string ends up with a special character that you can't see in it?
And even better way would be with an enum instead of Boolean.
public HandleType QueueHandleType {get;set;}
public enum HandleType {Normal, Special, SuperSpecial}
I might be misreading this, but is there any reason you can't just store the queues by name in array of Key/Value pairs, or even a Dictionary? For example:
var queues = new Dictionary<string, object>();
// add your queues to the dictionary, with the key name being your queue name
queues.Add("Queue1", myQueue);
// etc.
At that point you have a couple of options. First, you don't need to loop through the total set of queues you have -- you can simply do this:
var specialQueue = queues[mySpecialQueueString];
// do something with it
Or you can use LINQ to get any queues whose name contains your special string:
var results = queues.Where(keyValuePair => keyValuePair.Key.Contains(mySpecialString)).ToArray();
That said, Mason has a point in that you might need to worry about string matching and the like. There are, of course, several ways to go about this. If all queues have a fixed name then I like to make a NameConstants class with a bunch of static strings and refer to members of that class. Or you can do things like making them all upper and comparing to that.
I have a simple class that has bool property. The 'Get' logic for this property executes a stored procedure to return back a bit field from a database.
I then serialize this class and save it to an XML field in a database. It saves the class and the bool property just fine, no problem.
The problem I seem to be having is when I deserialize this class. The class deserilizes just fine, but when the data that drives the bool field has been updated, it seems that the class only recognizes what was serialized in XML, and it not looking back to the database to get the new bool value (does not execute my procedure to get the newly update bit field).
My solution has been to add the XmlIgnoreAttribute attribute to this field so it isn't serialized to begin with. But I'm wondering if anyone noticed this and/or can help me understand the inner working of .NET XmlSerializer class.
Thanks!
[XmlIgnoreAttribute]
public bool IsUpdated
{
get
{
DataTable dtResults = mclsSQLServerTool.LoadDataTable("exec stp_RL_SEL_NameIsUpdated '" + mstrName + "'");
bool blnIsUpdated = Convert.ToBoolean(dtResults.Rows[0]["RU_bitIsUpdated"]);
return blnIsUpdated;
}
}
The first thing to note here is that [XmlIgnore] is redundant; XmlSerializer is simply not interested in get-only properties (except for lists), because it knows it can't deserialize them. For example:
public class SomeType
{
public string Foo { get; set; }
public string Bar { get { Console.WriteLine("get_Bar"); return "abc"; } }
static void Main()
{
var ser = new XmlSerializer(typeof (SomeType));
ser.Serialize(Console.Out, new SomeType { Foo = "def" });
}
}
outputs (minus the namespaces aliases etc):
<SomeType>
<Foo>def</Foo>
</SomeType>
(note that Bar was not called)
For deserialization, the process (for simple values, not lists) is simple: as values are found in the the incoming xml stream, resolve them to members, and assign them - i.e. a xml-deserializer is basically a glorified switch statement based on incoming xml nodes.
It will never randomly call a "set" unless the data is in the incoming xml (and the property is read/write); and when it does, it expects to assign a value.
The interesting thing in your scenario is that your "get" doesn't assign the value anywhere - there is no cache. So actually, it doesn't matter that XmlSerializer doesn't touch it - every time you access IsUpdated it will do the query. Personally I suspect that is a mistake, and could lead to aggressive and unpredictable data querying.
Many serializers support the concept of serialization callbacks, which would allow you to perform some code at the end of serialization; however, XmlSerializer does not support this. So that isn't an option.
It isn't very clear what you want to achieve, but I'd just call a method at some point.
IMHO this is a mis-use of properties. Properties should have little or no code behind them. If this code was ever used in a client-server application you could potentially be making database calls from the client. I would recommend changing this to a method call. If you want to serialize the results then store the results of the "Convert.ToBoolean" in a property. Now it is a bit clearer as to what the property value is.
Something like this...
public bool IsUpdated { get; private set; }
public bool IsDataUpdated()
{
DataTable dtResults = mclsSQLServerTool.LoadDataTable("exec stp_RL_SEL_NameIsUpdated '" + mstrName + "'");
IsUpdated = Convert.ToBoolean(dtResults.Rows[0]["RU_bitIsUpdated"]);
return IsUpdated;
}
I am currently using a LINQ query to read an XML file e.g.
<MyObjects>
<MyObject>
<MyElement>some_text</MyElement>
<MyOtherElement>some_more_text</MyOtherElement>
</MyObject>
</MyObjects>
into a list of custom objects containing custom HistoryString properties. HistoryString contains 2 strings, a currentValue and a previousValue.
This all works great except when using XmlSerializer to write the custom objects back to an XML file, the output fairly obviously contains additional tags i.e.
<MyObjects>
<MyObject>
<MyElement>
<currentValue>some_text</currentValue>
<previousValue>some_text</previousValue>
</MyElement>
<MyOtherElement>
<currentValue>some_more_text</currentValue>
<previousValue>some_more_text</previousValue>
</MyOtherElement>
</MyObject>
</MyObjects>
Q: What would be the neatest and/or most efficient way of reading and writing XML in the same format, based on this fundamental difference?
Some initial ideas:
1) Mark the previousValue property with [System.Xml.Serialization.XmlIgnore] then sweep through the XML string that is to be written removing all traces of <currentValue> and </currentValue>
2) Open the existing file and manually make any updates/deletes/additions - this is surely more long winded.
3) Any way of having a HistoryString automatically resolve to its currentValue rather than serialize each of its properties, similar to how ToString() works?
I have done some research into this, including the useful MSDN articles here and here but I can't see any other attributes that would solve this problem, I am still unsure whether this is possible. Any ideas?
Here is another idea. If you define your class like so:
[Serializable]
public class MyObject
{
[XmlElement(ElementName = "MyElement")]
public string CurrentValueElement
{
get
{
return Element.CurrentValue;
}
set
{
Element = new MyElement
{
CurrentValue = value, PreviousValue = value
};
}
}
[XmlElement(ElementName = "MyOtherElement")]
public string CurrentValueOtherElement
{
get
{
return OtherElement.CurrentValue;
}
set {}
}
[XmlIgnore]
public MyElement Element { get; set; }
[XmlIgnore]
public MyElement OtherElement { get; set; }
}
Then, when the object is serialized, the output XML will look exactly like your example.
Also, if you extend the CurrentValueElement/CurrentValueOtherElement setter like this:
[XmlElement(ElementName = "MyElement")]
public string CurrentValueElement
{
get
{
return Element.CurrentValue;
}
set
{
Element = new MyElement
{
CurrentValue = value, PreviousValue = value
};
}
}
Then you'll be able to use the XmlSerializer to deserialize your objects directly without needing to resorting to LINQ.
Well why not serialize back using original schema and feeding into it the list of transformed objects from history using only current value?
e.g.
from h in HistoryEntryList
select new OriginalEntry{ field = h.field.current_value, ... };
I have a collection of simple elements in C#. Element is like:
public class Element
{
public string AName { get; private set; }
public string PName { get; private set; }
public string Value { get; set; }
}
I need to pass this collection as a string to a Python script. And in python for each element I need to call function like Func(AName, PName, Value). I've done this by serializing collection to JSON in C# and in python write code:
elements = json.loads(JSON)
for element in elements:
Func(routeLayer, element['AName'], element['PName'], element['Value'])
But now it turns out that I cannot use Python's json module. So I need a new way to pass this collection to script and dont use any additional modules. I am a real noob in Python, so solutions I can imagine are ugly. Can you give me an advice?
If there is some character which you can guarantee is not in the strings for AName,PName and Value then you could use that character as a separator.
In C#, you could "serialize" the information by simply joining the three strings with the separator, e.g. "foo,bar,baz".
Then in Python, the information could be deserialized with
aName,pName,value = element.split(',')
PS. Just out of curiosity, why can't you import modules in the standard library?
The most common standard, which should be available in Python as well is XML. Did you consider this option?
UPDATE:
You can use XmlSerializer in C# to serialize as XML. And here is something, what i've found for Python