JSON in C# (Part 1)

Comments [0]

There is something simple about JSON -- not that it can't be complex as well -- but simple data structures (where possible) should always be the goal. I was writing a simple test application for a web service and I wanted to save a history of the parameters I tested and also be able to use a combo box and retrieve past parameters. The web service had four parameters and I wanted to use a simple Javascript or Python style dictionary.

var parameter = {
    grName: "host1",
    grNode: "test",
    template: "$AnalogDevice",
    filter: ""
};

I looked at two C# possibilities from Jayrock and Newtonsoft, but my web service and test application were both ASP.NET 1.1 at the time and both of these options were for .NET 2.0. So I ended up using XML by modifying SerializableDictionary from Paul Welter for .NET 1.1 and I used an XmlFragmentWriter to write the parameters into the XmlDocument or to extract parameters from the document. The result is not very appealing:


    
        
            
                grNode
                node
            
            
                grName
                somename
            
            
                templateName
                $Well.WellHead.Choke
            
            
                xmlFilter
                
                    <filter>
                    <Attribute name="Area" include="1" />
                    <Attribute name="Tagname" include="1" />
                    <Attribute name="Description" include="1" />
                    <Attribute name="DeploymentStatus" include="1" />
                    </filter>
                
            
        
    

I suppose it would have been simpler to use a DataSet, but the whole point of the web service tester was to design an interface that would support multiple web methods that take different parameters that would have a different schema. I could have just written methods to read and write the dictionary keys and values under the SelectNodes("/xml/Parameters") nodes, but there is something appealing about a serialization mechanism. As much as I detested MFC, there was something very nice about the code: ar << m_oObject;  Now mind you, when I was trying to figure out how to make the (typically) binary serialization mechanism in MFC use XML instead (sometime around 1999), I wasn't pontificating on the wonders of MFC! I eventually figured out how to do that but it was a lot more challenging at the time than it needed to be!

Well, unfortunately I haven't had the time to try either of the JSON components, but I will write about it when I get the time. I'm still not sure if I will mix XML and JSON or just use JSON. Since I'm trying to compare, I will probably just use JSON. After all, the point of using XML is to be able to use the myriad of XML options and if you put JSON elements in XML, you've lost the native parsing capability.


    
        
        
            var filter = { grNode: "node", grName: "somename", templateName: "$Well.WellHead.Choke" }
        
    

You might as well choose all XML or all JSON:

var Parameters = [
    { grNode: "node", grName: "somename", templateName: "$WellHead.Well.Choke" },
    { grNode: "node2", grName: "somename2", templateName: "$WellHead.Well.Bore" },
];

Comment Section

Comments are closed.


A while ago I switched from Virtual Server 2005 R2 to VMware Server and so far I think it was a good switch. Unfortunately it wasn't without some consequences -- mainly that I should have read the procedure prior to converting my VM image. I had the Virtual Server extensions installed on my image and I forgot to remove them and then I seemed to be unable to remove them without doing extensive damage to the image. Now I am not able to install the VMware Tools as it causes the mouse to freak out and navigating through the keyboard is increasingly challenging these days, not to mention a pain.

I even went through the trouble of searching through the registry to find the Microsoft extensions and remove them. I also removed the C:\Program Files\Virtual Machine Additions folder and used Autoruns to remove the additions driver. The bottom line is -- don't bother -- it broke and wasn't worth fixing. Alas, I will have to put up with the annoyances (having to use Ctrl-Alt to release the cursor, finding out the system time is inaccurate after suspending the image, not being able to use the clipboard across the host and VM, etc.).

Since then I created a new image from scratch (for a different purpose so I still occasionally use the older image) and it is working great. Oh well, I'll get around to recreating the old image sometime soon.


Comment Section

Comments are closed.


I really like Subtext, but unfortunately I haven't had too much time to try to configure my blog. I've looked through the wiki, but haven't seen enough information to emulate the configuration of Subtext founder Phil Haack. For example, the FeedBurner plugin which lists the number of subscribers and links to get the RSS and even email feeds -- how exactly is that configured? I suppose I should RTFM but I'm having trouble finding it at the moment (not to mention I have way too much to do right now with work!). It seems the Subtext Wiki is describing plug-in features in Subtext 2.0 (I'm running 1.9.x currently -- 2.0 is not released yet) so that doesn't help.

Sooner or later I will install Phalanger and test out DokuWiki. I also want to install umbraco and possibly Rainbow portal while I'm at it. I'd love to have GNU Mailman installed to manage mailing lists, but there are too many issues related its dependence on UNIX-style usernames and groups and I would probably need to user IronPython for the Python functionality. I guess it is just too much work right now, especially as I am utterly swamped at work. Unfortunately at work I am stuck working on a design portion of a project and I don't even get to do "real" programming that much -- sometimes you long to launch Visual Studio or work on source code. Oh well, hopefully this phase will end soon.


Comment Section

Comments are closed.


A while back I needed to disable fields on a web page during submit and I found a utility written by Nancy Michell for MSDN Magazine. The utility covered the basic cases, but I found that I needed to use it for AJAX style calls as well as standard submits. So I modified the code to include an enable function as well. Finally I added exception handling around the disable and enable code to avoid pesky Javascript errors. The only thing I might change is to capture an array of the controls that were successfully disabled and then process only those controls on enable.

To use the disable utility, do the following:

// Method 1: Using the param string[] for controls
DisableHelper.DisableCtrlOnSubmit(this,
    "ServerForm.btnCancel",
    "ServerForm.btnSave");

// Method 2: Using the ArrayList of controls
aryDisableControls = new ArrayList();
aryDisableControls.Add("ConfigLoopForm.btnCancel");
aryDisableControls.Add("ConfigLoopForm.btnSave");
DisableHelper.DisableCtrlOnSubmit(this, aryDisableControls);

Here is the utility source:

using System;
using System.Collections;
using System.Text;

namespace Utility.Web.UI
{
    public class DisableHelper
    {
        /// 
        /// Adds client-side javascript code to disable controls on a submit.
        /// 
        /// Reference to the current page
        /// String array of client-side control names.
        public static void DisableCtrlOnSubmit(
            System.Web.UI.Page Page,
            params string[] strControls)
        {
            StringBuilder sbJS = new StringBuilder(200);
            sbJS.Append(@"

"
                );

            if (!Page.IsClientScriptBlockRegistered("MSDisableImpl"))
            {
                Page.RegisterClientScriptBlock(
                    "MSDisableImpl",
                    sbJS.ToString());
            }

            Page.RegisterOnSubmitStatement("MSDisable", "MSDisable(false);");
        }

        /// 
        /// Adds client-side javascript code to disable controls on a submit.
        /// 
        /// Reference to the current page
        /// ArrayList with the client-side control names
        public static void DisableCtrlOnSubmit(
            System.Web.UI.Page Page,
            ArrayList aryClientControlNames)
        {
            if (aryClientControlNames != null)
            {
                string[] strControls = (string[]) aryClientControlNames.ToArray(typeof(string));
                DisableCtrlOnSubmit(Page, strControls);
            }
        }

        private static void DiableCtrlOnSubmit(
            System.Web.UI.Page Page,
            ArrayList aryClientControlNames)
        {
            DisableCtrlOnSubmit(Page, aryClientControlNames);
        }
    }
}

Comment Section

Comments are closed.


I vastly prefer Javascript over VBScript for many of the reasons describe by Joel On Software and by this MSDN article. Many projects that I work on use Excel for simple data relationships. Most of the data is so simple that it does not warrant a full SQL database, but a single table (spreadsheet) will suffice. I use the spreadsheets to generate files for multiple systems in some cases. For example, in an embedded turbine governor project I used a spreadsheet to map the tagnames to the communication details (rSpeed1 = 32025). The spreadsheet generated a C source file for the embedded controller, an XML file for the configuration software, and a mapping file for the help documentation. Using the spreadsheet helped keep three distinct systems synchronized.

I still receive a lot of Excel files from customers and I usually need to process and filter the spreadsheets. I receive files with DCS tag names and I often need to filter the list. The auto-filter in Excel is fine for two or three types, but I usually need to filter based on more than ten regular expression filters. The following is an example of a Javascript file I used to filter the spreadsheet:

// Constants
var INITIAL_ROW = 3;                    // Two rows of header
var DELETED_COLUMN_NAME = "DELETED";
var FILTER_COLUMN_NAME = "INST_NO";
var INCLUDE_COLUMN = [
     "INST_NO"
    ,"PID_SHT"
    ,"INTOOLS__Loop_No"
    ,"INST"
    ,"DESCRP1"
    ,"DESCRP2"
    ,"HI_SCALE"
    ,"ENG_UNITS"
    ,"ALMLL"
    ,"ALML"
    ,"ALMH"
    ,"ALMHH"
    ,"SET_POINT"
];
var INCLUDE_TAGPREFIX = [
    // Analog Inputs
     "AI"
    ,"FI"
    ,"LI"
    ,"PI"
    ,"TI"

    // DCS Calculations
   
    // Controllers
    ,"AC"
    ,"FC"
    ,"LC"
    ,"PC"
    ,"TC"
    ,"AIC"
    ,"FIC"
    ,"LIC"
    ,"PIC"
    ,"TIC"
   
    // Hand Switch PV Flag
    ,"HS"
    ,"PVFL"
];


// Basic Excel object
var Excel = {
    _App : null
    ,_created : false
    ,Constants : {
         pause : 0
        //Excel.XlDeleteShiftDirection
        ,xlShiftToLeft    : 0xFFFFEFC1
        ,xlShiftUp        : 0xFFFFEFBE
    }

    ,newApp : function() {
        this._App = new ActiveXObject("Excel.Application");
        this._created = true;
        return this._App;
    }

    ,getApp : function() {
        this._App = GetObject("","Excel.Application");
        return this._App;
    }

    ,_quit : function() {
        if (this._App)
        {
            this._App.Quit();
            this._created = false;
            this._App = null;
        }
        return;
    }

    ,bye : function() {
        if (this._created)
            this._quit();
        this._App = null;
    }
};


// Load the header row
function getHeaderRow(sheet, table)
{
    if (sheet)
    {
        var row = 1;
        var lastCol = sheet.UsedRange.Columns.Count;

        for (var col = 1; col <= lastCol; ++col)
        {
            var title = "" + sheet.Cells(row, col).Text;
            table[title] = col;
            table["_" + col] = title;
        }
    }
}

function ProcessWorksheet(sheet, rowFunction, map)
{
    if (sheet)
    {
        var lastRow = sheet.UsedRange.Rows.Count;
        var row = INITIAL_ROW;
        while (row <= lastRow)
        {
            // If the row does not match, it is deleted and
            // do not move to the next row as the rows are shifted up.
               if ( rowFunction(sheet, row, map) )
                   ++row;
               else
                   --lastRow;
           }
    }
}

function matchTagPrefixSetup(prefixArray)
{
    var result;
    if (prefixArray)
    {
        result = new Array(prefixArray.length);
        for (var i = 0; i < prefixArray.length; ++i)
        {
            result[i] = new RegExp("^" + prefixArray[i] + "[0-9]");
        }
    }
    return result;
}

// Hide row if it does not match any filter prefix
function processRowFunction(sheet, row, data)
{
    var match = false;
    data.rowsProcessed += 1;
    var text = sheet.Cells(row, data.deleteCol).Value;
    if (!text)
    {
        text = sheet.Cells(row, data.matchCol).Value;
        for (var i = 0; i < data.prefixes.length; ++i)
        {
            var a = data.prefixes[i].exec(text);
            if (a != null)
            {
                match = true;
                break;
            }
        }
    }

    if (!match)
    {
        sheet.Cells(row, data.matchCol).EntireRow.Delete(Excel.Constants.xlShiftUp);
        data.rowsDeleted += 1;
    }
   
    return match;
}

function main(args)
{
    // Attempt to get an open instance of Excel
    var app = Excel.getApp();
    if (app)
        var sheet = app.ActiveSheet;

    if (sheet)
    {
        // Get an array of RegExp for the matching prefixes
        var prefixes = matchTagPrefixSetup(INCLUDE_TAGPREFIX);

        // Get the header row
        var table = new Object();
        getHeaderRow(sheet, table);
        var matchCol = table[FILTER_COLUMN_NAME];
        var deleteCol = table[DELETED_COLUMN_NAME];

        // Build the row process function
        var map =
        {
             "prefixes"        : prefixes
            ,"matchCol"        : matchCol
            ,"deleteCol"       : deleteCol
            ,"rowsProcessed"   : 0
            ,"rowsDeleted"     : 0
            ,"startTime"       : null
            ,"finishTime"      : null
            ,"elapsedTime"     : null
        };

        map.startTime = new Date();

        // Process the worksheet
        app.ScreenUpdating = false;
        ProcessWorksheet(sheet, processRowFunction, map);
        app.ScreenUpdating = true;

        map.finishTime = new Date();
        map.elapsedTime = map.finishTime - map.startTime;
       
        // Statistics
        WScript.Echo("Rows Processed: " + map.rowsProcessed);
        WScript.Echo("Rows Deleted: " + map.rowsDeleted);
        WScript.Echo("Elapsed Time: " + map.elapsedTime / 1000 + " seconds");
    }

    // Cleanup
    sheet = null;
    myApp = null;
    Excel.bye();
}

// Program entry point
main(WScript.Arguments);

Comment Section

Comments are closed.


<< Older Posts | Newer Posts >>