I have an XML file with the follow in it:
<tooltip>
<text>My Tool Tip</text>
<color>#000000</color>
<crosshairs>
<width>3</width>
<color>green</color>
<padding>5px</padding>
</crosshairs>
<crosshairs>
<width>3</width>
<color>blue</color>
</crosshairs>
</tooltip>
I wish to load the "crosshairs" elements into a C# object - the object is then serialized out to a JSON file:
ToolTipClass toolTip = new ToolTipClass(xmlDoc);
JavaScriptSerializer s = new JavaScriptSerializer();
string aJSON = s.Serialize(toolTip);
// aJSON is now written out to file.
I do have a "toolTip" class with some public variables which are set by default in the constructor. The constuctor also reads the XML file in and overwrites the public variables with the value in the XML (e.g. is written into "public string text;", but the "crosshairs" part of the tool tip can contain either 1 or several elements - I don't want to define a class for the crosshairs as the tags within the crosshairs can be more than just the 2 defined above (width, color). eg (padding, margin, fontSize etc.)
the JSon output would look something like this:
tooltip: {
text: "My Tool Tip",
color: "#000000",
crosshairs: [{
width: 3,
color: 'green',
padding: '5px'
}, {
width: 3,
color: 'blue'
}]
}
What I need to know is How do I load the crosshairs elements into an object that isn't predefiend?
I have looked at: http://danielwylie.me/blog/2010/04/26/c-convert-xml-to-an-object-or-list-of-an-object/?Focus=Yes
but this uses a "person" class which is not what I wanted.
Many thanks for any help/pointers.
Extra:
ToolTip Class:
public class ToolTip
{
public string backgroundColor = "rgba(255, 255, 255, .80)";
public string borderColor = "#764D9B";
public int borderRadius = 5;
public int borderWidth = 1;
//public string crosshairs = null;
//public List<object> crosshairs = new List<object>();
public Boolean enabled = true;
//formatter: ;
public Boolean shadow = true;
public Boolean shared = false;
public float snap = 10;
public HCCSS style = new HCCSS();
public string text = "[undefined]";
public string color = "#000000";
public HCToolTip(XmlDocument xmlDoc) {
text = findTagInXML_String(xmlDoc, "//tooltip/text", text);
color = findTagInXML_String(xmlDoc, "//tooltip/color", color);
//snip
}
static private string findTagInXML_String(XmlDocument xmlDoc, string tag, string defaultvalue) {
return xmlDoc.SelectSingleNode(tag) == null || xmlDoc.SelectSingleNode(tag).InnerText == "null" ? defaultvalue : xmlDoc.SelectSingleNode(tag).InnerText;
}
}
update / reply ## (not sure how to do replies below).
Thanks for the code and the link to the converter site. I've added in some code and sort of got it doing something, but I do have a few more problems.
How do I get the XML data into the Crosshairs collection. I've currently got this in my toolTip constructor class:
Crosshairs c = new Crosshairs();
c.SetProperty("a",new {width="3", color="green"});
c.SetProperty("b", new { width = "3", color = "blue" });
crosshairs.Add(c);
I'm assuming where I've got the new width color is where I would want it to bring in the details from the XML file mentioned above.
I've added in the Converter class but the output I'm now getting is something like:
tooltip: {
borderColor: "#F00"
crosshairs: {
List: [
{
Value: {
width: "3"
color: "green"
}
Text: {
width: "3"
color: "blue"
}
}
]
}
enabled: true
}
I did change the example converter to have the following rows:
foreach (Crosshairs item in listType) {
//Add each entry to the dictionary.
Dictionary<string, object> listDict = new Dictionary<string, object>();
listDict.Add("Value", item.GetProperty("a"));
listDict.Add("Text", item.GetProperty("b"));
itemsList.Add(listDict);
}
result["List"] = itemsList;
As you can see it' doesn't look very generic as it uses types of "CrosshairsCollection", but I'm sort of guessing that I can change the "CrosshairsCollection" to a "GenericCollection" as it's only a dictionary - so #1 above still applies.
The other problem is that not only the toolTip has this "crosshairs" class, but I do have other classes that are similar to crosshairs - eg style which I would like to have the same system.
If anyone could help with #1 above - importing the data from XML - to generic class rather than a predefined class it would really help.
Again - Many thanks for the help.
Alan
Define a simple Crosshairs type that is a wrapper for a string, object dictionary:
class CrosshairsCollection : List<Crosshairs>
{
}
class Crosshairs
{
private Dictionary<string, object> dict = new Dictionary<string,object>();
public IEnumerable<KeyValuePair<string, object>> GetAllProperties()
{
foreach (string key in dict.Keys)
{
yield return new KeyValuePair<string, object>(key, dict[key]);
}
}
public object GetProperty(string s)
{
object value;
bool exists = dict.TryGetValue(s, out value);
if (!exists)
{
value = null;
}
return value;
}
public void SetProperty(string s, object o)
{
if (!dict.ContainsKey(s))
{
dict.Add(s, o);
}
else
{
dict[s] = o;
}
}
}
Then implement a JavaScriptConverter for CrosshairsCollection, similar to this: http://msdn.microsoft.com/en-us/library/system.web.script.serialization.javascriptconverter.aspx
Replace your List with CrosshairsCollection and register the JavaScriptConverter with your JavaScriptSerializer instance.
[Edit: answers to follow up questions]
I'm assuming that you have two follow-up questions, both numbered 1, and that the second one is "how do I get the JSON output to be more like my original example?" :)
The way I interpreted your original XML was that a tooltip has multiple crosshairs, and not a single crosshairs with multiple, overlapping property definitions. The representation in the XML and your name usage in the code aren't in accord though: you call the entire thing "crosshairs." Bear that in mind as you're reading my original and edited response.
So to get an object that mimics the original XML, I would have expected you to provide the properties like so:
CrosshairsCollection crosshairsList = new CrosshairsCollection();
Crosshairs c1 = new Crosshairs();
c1.SetProperty("width", 3);
c1.SetProperty("color", "green");
c1.SetProperty("padding", "5px");
crosshairsList.Add(c1);
Crosshairs c2 = new Crosshairs();
c2.SetProperty("width", 3);
c2.SetProperty("color", "blue");
crosshairsList.Add(c2);
Take that one step further, turn each Crosshairs initialization into a factory method, and plug it into your XML representation, like you're doing in the HCToolTip constructor.
Then, you can add each name value pair into the JSON output as they are:
foreach (Crosshairs crosshairs in crosshairsList) {
Dictionary<string, object> crosshairProps = new Dictionary<string, object>();
foreach (KeyValuePair<string, object> prop in crosshairs.GetAllProperties()) {
crosshairProps.Add(prop.Key, prop.Value);
}
itemsList.Add(crosshairProps);
}
result["crosshairs"] = itemsList;
You might need to implement a JavaScriptConverter one level higher up, on ToolTip, in order to get an anonymous list as the property value for ToolTip.crosshairs.
I hope what this is showing though is that really all you're looking for is a key/value collection. I named the classes CrosshairsCollection and Crosshairs to try to draw the distinction between a list of <crosshairs> tags and the subtree of a <crosshairs> tag. But you could name them MultiPropertyItemCollection and MultiPropertyItem, or whatever, because there's nothing type specific in the implementation.
Related
I have a dynamic object which basically holds an AvroRecord. AvroRecord class details here.
I can assign values to the properties statically but I was wondering if this could be done dynamically. I have looked at the forum question here ,here and also here. But none of these work for me.
This is the static code that works.
var serializer = AvroSerializer.CreateGeneric(Schema);
var rootSchema = serializer.WriterSchema as RecordSchema;
dynamic counterpartRow = new AvroRecord(rootSchema);
counterpartRow.CounterpartID = Row.CounterpartID
counterpartRow.CounterpartFirstDepositDate = Row.CounterpartFirstDepositDate
The Row is an object of the InputBuffer class of SSIS and it holds all the columns coming from the upstream data source.
The schema variable used above is an avro schema, which is something like this.
Schema = #"{
""type"":""record"",
""name"":""Microsoft.Hadoop.Avro.Specifications.Counterparts"",
""fields"":
[
{ ""name"":""CounterpartID"", ""type"":""int"" },
{ ""name"":""CounterpartFirstDepositDate"", ""type"":[""string"",""null""] },
{ ""name"":""CounterpartFirstTradeDate"",""type"":[""string"",""null""] },
{ ""name"":""ClientSegmentReportingID"",""type"":""int"" },
{ ""name"":""ClientSegmentReportingName"", ""type"":[""string"",""null""] },
{ ""name"":""ContractID"", ""type"":""int""},
{ ""name"":""ContractFirstDepositDate"", ""type"":[""string"",""null""]},
{ ""name"":""ContractFirstTradeDate"",""type"":[""string"",""null""] },
{ ""name"":""ContractClosingOffice"",""type"":[""string"",""null""] },
{ ""name"":""LeadCreationDate"", ""type"":[""string"",""null""] },
{ ""name"":""ContractCountryOfResidence"", ""type"":[""string"",""null""]}
]
}";
I have tried something like the earlier forum links suggested like
counterpartRow.GetType().GetField("CounterpartID").SetValue(Row, Row.CounterpartID, null);
and also the other method (which apparently should work for for dynamic type), but even that does not.
foreach (string propertyName in GetPropertyKeysForDynamic(counterpartRow.Schema.Fields()))
{
string propertyValue = counterpartRow[propertyName];
}
and the function defined like this.
public List<string> GetPropertyKeysForDynamic(dynamic dynamicToGetPropertiesFor)
{
var jObject = (JObject)JToken.FromObject(dynamicToGetPropertiesFor);
Dictionary<string, object> values = jObject.ToObject<Dictionary<string, object>>();
List<string> toReturn = new List<string>();
foreach (string key in values.Keys)
{
toReturn.Add(key);
}
return toReturn;
}
The dictionary above returns blank.
the Row mentioned above is an object of InputBuffer class (auto generated class in SSIS).
which is something like this.
public class Input0Buffer: ScriptBuffer
{
public Input0Buffer(PipelineBuffer Buffer, int[] BufferColumnIndexes, OutputNameMap OutputMap)
: base(Buffer, BufferColumnIndexes, OutputMap)
{
}
public Int32 CounterpartID
{
get
{
return Buffer.GetInt32(BufferColumnIndexes[0]);
}
}
------more properties
If you see my original static code, I am trying to dynamically generate the assignment. I have already dynamically generated the schema (instead of the static definition I have given above). So, the only piece left to generate dynamically is the assignment of the assignment. An idea could be that I generate the string but how do I then execute that string? That is only if there is no way to achieve this.
If I understand correct your question I think you can try something like this
string _schema = "your avro schema"
RecordSchema _record = (RecordSchema)Avro.Schema.Parse(_schema);
GenericRecord _generic_record = new GenericRecord(payload_record);
for (int ii = 0; ii < _record.Fields.Count; ii++)
{
_generic_record.Add(_record.Fields[ii].Name, Raw.TheFiledYouNeed);
}
I'm trying to send a JSON message to facebook that they call a game.achievement but I wanted to create the object using an AnonymousType before Posting. The problem is one of the fields is "game:points" (with the colon). As you can see I've used the # prefix for the object field but it doesn't work for the game:points field. It gets underlined in red and won't compile.
var paramsJson = new
{
privacy = new { value = "ALL_FRIENDS" },
#object = new
{
app_id = "my app id",
type = "game.achievement",
title = "Test",
#"game:points" = 100,
description = "Test",
image = "img.png"
}
};
I've tried many varieties of #, double quotes etc. Is it possible or do I need to just use a StringBuilder for this?
Why don't you just use a dictionary?
var postData = new Dictionary<string, object>
{
{"game:points", 100}
}
Presumably you're going to have to serialize your object to Post it regardless of how it's constructed. Serializing this to JSON would result in the same structure. Alternatively, just do as other's have suggested and create a class for your payload. You can then use Newtonsoft to indicate alternative names for serialization.
public class GameAchievement
{
[JsonProperty("game:points")]
public int Points {get; set;}
}
At the moment I'm adding functionality to our service that will take in an object that is about to be logged to trace and mask any sensitive fields that are included in the object.
The issue is that we can get objects with different layers. The code I have written so far only handles a parent field and a single child field and uses a nasty embedded for loop implementation to do it.
In the event that we have a third embedded layer of fields in an object we want to log, this wouldn't be able to handle it at all. There has to be a more efficient way of handling generic parsing of a dynamic object, but so far it's managed to avoid me.
The actual code that deserializes and then masks field sin the object looks like this:
private string MaskSensitiveData(string message)
{
var maskedMessage = JsonConvert.DeserializeObject<dynamic>(message);
LoggingProperties.GetSensitiveFields();
for (int i = 0; i < LoggingProperties.Fields.Count(); i++)
{
for (int j = 0; j < LoggingProperties.SubFields.Count(); j++)
{
if (maskedMessage[LoggingProperties.Fields[i]] != null)
{
if (maskedMessage[LoggingProperties.Fields[i]][LoggingProperties.SubFields[j]] != null)
{
maskedMessage[LoggingProperties.Fields[i]][LoggingProperties.SubFields[j]] = MaskField(LoggingProperties.SubFieldLengths[j]);
}
}
}
}
return maskedMessage.ToString(Formatting.None);
}
And it works off of a LoggingProperties class that looks like this:
public static class LoggingProperties
{
// Constants indicating the number of fields we need to mask at present
private const int ParentFieldCount = 2;
private const int SubFieldCount = 4;
// Constant representing the character we are using for masking
public const char MaskCharacter = '*';
// Parent fields array
public static string[] Fields = new string[ParentFieldCount];
// Subfields array
public static string[] SubFields = new string[SubFieldCount];
// Array of field lengths, each index matching the subfield array elements
public static int[] SubFieldLengths = new int[SubFieldCount];
public static void GetSensitiveFields()
{
// Sensitive parent fields
Fields[0] = "Parent1";
Fields[1] = "Parent2";
// Sensitive subfields
SubFields[0] = "Child1";
SubFields[1] = "Child2";
SubFields[2] = "Child3";
SubFields[3] = "Child4";
// Lengths of sensitive subfields
SubFieldLengths[0] = 16;
SubFieldLengths[1] = 16;
SubFieldLengths[2] = 20;
SubFieldLengths[3] = 3;
}
}
}
The aim was to have a specific list of fields for the masking method to look out for that could be expanded or contracted along with our systems needs.
The nested loop method though just seems a bit roundabout to me. Any help is appreciated.
Thanks!
UPDATE:
Here's a small example of a parent and child record that would be in the message prior to the deserialize call. For this example say I'm attempting to mask the currency ID (So in properties the fields could be set like this: Parent1 = "Amounts" and Child1 = "CurrencyId"):
{
"Amounts":
{
"Amount":20.0,
"CurrencyId":826
}
}
An example of a problem would then be if the Amount was divided into pounds and pence:
{
"Amounts":
{
"Amount":
{
"Pounds":20,
"Pence":0
},
"CurrencyId":826
}
}
This would another layer and yet another embedded for loop...but with that I would be making it overly complex and difficult if the next record in a message had only two layers.
Hope this clarifies a few things =]
Okay, I've really tried but I couldn't figure out an elegant way. Here's what I did:
The first try was using reflection but since all the objects are of type JObject / JToken, I found no way of deciding whether a property is an object or a value.
The second try was (and still is, if you can figure out a good way) more promising: parsing the JSON string into a JObject with var data = JObject.Parse(message) and enumerating its properties in a recursive method like this:
void Mask(data)
{
foreach (JToken token in data)
{
if (token.Type == JTokenType.Object)
{
// It's an object, mask its children
Mask(token.Children());
}
else
{
// Somehow mask it but I couldn't figure out to do it with JToken
// Pseudocode, it doesn't actually work:
if (keysToMask.Contains(token.Name))
token.Value = "***";
}
}
}
Since it doesn't work with JTokens, I've tried the same with JProperties and it works for the root object, but there's a problem: although you can see if a given JProperty is an object, you can not select its children object, JProperty.Children() gives JToken again and I found no way to convert it to a JProperty. If anyone knows how to achieve it, please post it.
So the only way I found is a very dirty one: using regular expressions. It's all but elegant - but it works.
// Make sure the JSON is well formatted
string formattedJson = JObject.Parse(message).ToString();
// Define the keys of the values to be masked
string[] maskedKeys = {"mask1", "mask2"};
// Loop through each key
foreach (var key in maskedKeys)
{
string original_pattern = string.Format("(\"{0}\": )(\"?[^,\\r\\n]+\"?)", key);
string masked_pattern = "$1\"censored\"";
Regex pattern = new Regex(original_pattern);
formatted_json = pattern.Replace(formatted_json, masked_pattern);
}
// Parse the masked string
var maskedMessage = JsonConvert.DeserializeObject<dynamic>(formatted_json);
Assuming this is your input:
{
"val1" : "value1",
"val2" : "value2",
"mask1" : "to be masked",
"prop1" : {
"val3" : "value3",
"val1" : "value1",
"mask2" : "to be masked too",
"prop2" : {
"val1" : "value 1 again",
"mask1" : "this will also get masked"
}
}
}
This is what you get:
{
"val1": "value1",
"val2": "value2",
"mask1": "censored",
"prop1": {
"val3": "value3",
"val1": "value1",
"mask2": "censored",
"prop2": {
"val1": "value 1 again",
"mask1": "censored"
}
}
}
I have a form where I collect data from users. When this data is collected, I pass it to various partners, however each partner has their own rules for each piece of data, so this has to be converted. I can make this happen, but my worries are about the robustness. Here's some code:
First, I have an enum. This is mapped to dropdown a dropdown list - the description is the text value, and the int mapped to the value.
public enum EmploymentStatusType
{
[Description("INVALID!")]
None = 0,
[Description("Permanent full-time")]
FullTime = 1,
[Description("Permanent part-time")]
PartTime = 2,
[Description("Self employed")]
SelfEmployed = 3
}
When the form is submitted, the selected value is converted to its proper type and stored in another class - the property looks like this:
protected virtual EmploymentStatusType EmploymentStatus
{
get { return _application.EmploymentStatus; }
}
For the final bit of the jigsaw, I convert the value to the partners required string value:
Dictionary<EmploymentStatusType, string> _employmentStatusTypes;
Dictionary<EmploymentStatusType, string> EmploymentStatusTypes
{
get
{
if (_employmentStatusTypes.IsNull())
{
_employmentStatusTypes = new Dictionary<EmploymentStatusType, string>()
{
{ EmploymentStatusType.FullTime, "Full Time" },
{ EmploymentStatusType.PartTime, "Part Time" },
{ EmploymentStatusType.SelfEmployed, "Self Employed" }
};
}
return _employmentStatusTypes;
}
}
string PartnerEmploymentStatus
{
get { return _employmentStatusTypes.GetValue(EmploymentStatus); }
}
I call PartnerEmploymentStatus, which then returns the final output string.
Any ideas how this can be made more robust?
Then you need to refactor it into one translation area. Could be something like a visitor pattern implementation. Your choices are distribute the code (as you are doing now) or visitor which would centralize it. You need to build in a degree of fragility so your covering tests will show problems when you extend in order to force you to maintain the code properly. You are in a fairly common quandry which is really a code organisational one
I did encounter such a problem in one of my projects and I solved it by using a helper function and conventions for resource names.
The function is this one:
public static Dictionary<T, string> GetEnumNamesFromResources<T>(ResourceManager resourceManager, params T[] excludedItems)
{
Contract.Requires(resourceManager != null, "resourceManager is null.");
var dictionary =
resourceManager.GetResourceSet(culture: CultureInfo.CurrentUICulture, createIfNotExists: true, tryParents: true)
.Cast<DictionaryEntry>()
.Join(Enum.GetValues(typeof(T)).Cast<T>().Except(excludedItems),
de => de.Key.ToString(),
v => v.ToString(),
(de, v) => new
{
DictionaryEntry = de,
EnumValue = v
})
.OrderBy(x => x.EnumValue)
.ToDictionary(x => x.EnumValue, x => x.DictionaryEntry.Value.ToString());
return dictionary;
}
The convention is that in my resource file I will have properties that are the same as enum values (in your case None, PartTime etc). This is needed to perform the Join in the helper function which, you can adjust to match your needs.
So, whenever I want a (localized) string description of an enum value I just call:
var dictionary = EnumUtils.GetEnumNamesFromResources<EmploymentStatusType>(ResourceFile.ResourceManager);
var value = dictionary[EmploymentStatusType.Full];
How can I create a binding to a sub-property of a specific element in a list?
I've created a class that exposes an IList property:
public IList<VideoChannel> VideoChannels {
get {
const int NumVideoChannels = 4;
return (new List<VideoChannel>(NumVideoChannels) {
new VideoChannel("Channel 1") {
VideoActive = !_rawData[Main][0x04].BitIsSet(0),
OutOfRange = !_rawData[Main][0x05].BitIsSet(0) },
new VideoChannel("Channel 2") {
VideoActive = !_rawData[Main][0x04].BitIsSet(1),
OutOfRange = !_rawData[Main][0x05].BitIsSet(1) },
new VideoChannel("Channel 3") {
VideoActive = !_rawData[Main][0x04].BitIsSet(2),
OutOfRange = !_rawData[Main][0x05].BitIsSet(2) },
new VideoChannel("Channel 4") {
VideoActive = !_rawData[Main][0x04].BitIsSet(3),
OutOfRange = !_rawData[Main][0x05].BitIsSet(3) },
}).AsReadOnly();
}
set { ;}
}
I've also created an 'LED' UserControl with a single boolean property ('LedOn') that determines the colour of the led.
I want to create 8 'LED' controls, each of which is bound to a specific 'VideoActive' or 'OutOfRange' property in the IList above.
This doesn't seem to work:
ledVideoActiveChannel1.DataBindings.Add("LedOn", _myDevice, "VideoChannels[0].VideoActive");
ledOutOfRangeChannel1.DataBindings.Add("LedOn", _myDevice, "VideoChannels[0].OutOfRange");
The error is "Child list for field VideoChannels[0] cannot be created."
I'm relatively new to C# and OOP in general, so forgive me if this is a trivial question.
Thanks!
How about this:
ledVideoActiveChannel1.DataBindings.Add("LedOn", _myDevice.VideoChannels[0], "VideoActive");
ledOutOfRangeChannel1.DataBindings.Add("LedOn", _myDevice.VideoChannels[0], "OutOfRange");
That, and make sure your VideoChannel class implements INotifyPropertyChanged.
that would be a magical "magic string" ;-)
You can create separate property like so:
public bool OutOfRange
{
get{ return VideoChannels[0].OutOfRange; }
}
Then
ledOutOfRangeChannel1.DataBindings.Add("LedOn", _myDevice, "OutOfRange");
You would want to add null checking also...