function in JSON format - possible? - c#

I have the below JSON (been snipped for space), as you can see in the "test" and "tooltip" I have a property that needs to contain a function "formatter" (note this JSON is read in from an XML file and converted to JSON in .NET)
{
"test": {
"formatter": function(){return '<b>'+ this.point.name +'<\/b>: '+ this.y +' %';}
},
"title": {
"align": "center",
"text": "Your chart title here"
},
"tooltip": {
"formatter": function(){return '<b>'+ this.point.name +'<\/b>: '+ this.y +' %';}
}
}
Unfortunatly I'm getting an error on the ASPX page that produces the JSON file
There was an error parsing the JSON document. The document may not be well-formed.
This error is due to the fact that the bit after the "formatter" is not in quotation marks as it thinks it's a string. but If I put a string around it then the front end html page that uses the JSON doesn't see the function.
Is it possible to pass this as a function and not a string?
Many thanks.
Edit:
Thanks for the quick replys. As I said I know that the above isn't correct JSON due to the fact that the "function(){...}" part isn't in quote marks. The front end that reads the JSON file is 3rd party so I was wondering how I could pass the function through, I understand about the problems of injection (from a SQL point of view) and understand why it's not possible in JSON (not worked with JSON before).

If you passed it as a string you could use Javascripts EVAL function, but EVAL is EVIL.
What about meeting it half way and using Object Notation format ?
This is a template jquery plugin that I use at work, the $.fn.extend shows this notation format.
/*jslint browser: true */
/*global window: true, jQuery: true, $: true */
(function($) {
var MyPlugin = function(elem, options) {
// This lets us pass multiple optional parameters to your plugin
var defaults = {
'text' : '<b>Hello, World!</b>',
'anotherOption' : 'Test Plugin'
};
// This merges the passed options with the defaults
// so we always have a value
this.options = $.extend(defaults, options);
this.element = elem;
};
// Use function prototypes, it's a lot faster.
// Loads of sources as to why on the 'tinternet
MyPlugin.prototype.Setup = function()
{
// run Init code
$(this.element).html(this.options.text);
};
// This actually registers a plugin into jQuery
$.fn.extend({
// by adding a jquery.testPlugin function that takes a
// variable list of options
testPlugin: function(options) {
// and this handles that you may be running
// this over multiple elements
return this.each(function() {
var o = options;
// You can use element.Data to cache
// your plugin activation stopping
// running it again;
// this is probably the easiest way to
// check that your calls won't walk all
// over the dom.
var element = $(this);
if (element.data('someIdentifier'))
{
return;
}
// Initialise our plugin
var obj = new MyPlugin(element, o);
// Cache it to the DOM object
element.data('someIdentifier', obj);
// Call our Setup function as mentioned above.
obj.Setup();
});
}
});
})(jQuery);

Related

Usage of Document Extraction cognitive skill

I am trying to utilize Azure Cognitive services to perform basic document extraction.
My intent is to input PDFs and DOCXs (and possibly some other files) into the Cognitive Engine for parsing, but unfortunately, the implementation of this is not as simple as it seems.
According to the documentation (https://learn.microsoft.com/en-us/azure/search/cognitive-search-skill-document-extraction#sample-definition), I must define the skill and then I should be able to input files, but there is no examples on how this should be done.
So far I have been able to define the skill but I am still not sure where I should be dropping the files into.
Please see my code below, as it seeks to replicate the same data structure shown in the example code (albeit using the C# Library)
public static DocumentExtractionSkill CreateDocumentExtractionSkill()
{
List<InputFieldMappingEntry> inputMappings = new List<InputFieldMappingEntry>
{
new("file_data") {Source = "/document/file_data"}
};
List<OutputFieldMappingEntry> outputMappings = new List<OutputFieldMappingEntry>
{
new("content") {TargetName = "extracted_content"}
};
DocumentExtractionSkill des = new DocumentExtractionSkill(inputMappings, outputMappings)
{
Description = "Extract text (plain and structured) from image",
ParsingMode = BlobIndexerParsingMode.Text,
DataToExtract = BlobIndexerDataToExtract.ContentAndMetadata,
Context = "/document",
};
return des;
}
And then I build on this skill like so:
_indexerClient = new SearchIndexerClient(new Uri(Environment.GetEnvironmentVariable("SearchEndpoint")), new AzureKeyCredential(Environment.GetEnvironmentVariable("SearchKey"));
List<SearchIndexerSkill> skills = new List<SearchIndexerSkill> { Skills.DocExtractionSkill.CreateDocumentExtractionSkill() };
SearchIndexerSkillset skillset = new SearchIndexerSkillset("DocumentSkillset", skills)
{
Description = "Document Cracker Skillset",
CognitiveServicesAccount = new CognitiveServicesAccountKey(Environment.GetEnvironmentVariable("CognitiveServicesKey"))
};
await _indexerClient.CreateOrUpdateSkillsetAsync(skillset);
And... then what?
There is no clear method that would fit what I believe the next stage, actually parsing documents.
What is the next step from here to begin dumping files into the _indexerClient (of type SearchIndexerClient)?
As the next stage shown in the documentation is:
{
"values": [
{
"recordId": "1",
"data":
{
"file_data": {
"$type": "file",
"data": "aGVsbG8="
}
}
}
]
}
Which is not clear as to where I would be doing this.
According to the document that you have mentioned. They are actually trying to get the output through postman. They are using a GET Method to receive the extracted document content by sending JSON request to the mentioned URL(i.e. Cognitive skill url) and the files/documents are needed to be uploaded to your storage account in order to get extracted.
you can follow this tutorial to get more insights.

How to set MongoDB Change Stream 'OperationType' in the C# driver?

When running the new MongDB Server, version 3.6, and trying to add a Change Stream watch to a collection to get notifications of new inserts and updates of documents, I only receive notifications for updates, not for inserts.
This is the default way I have tried to add the watch:
IMongoDatabase mongoDatabase = mongoClient.GetDatabase("Sandbox");
IMongoCollection<BsonDocument> collection = mongoDatabase.GetCollection<BsonDocument>("TestCollection");
var changeStream = collection.Watch().ToEnumerable().GetEnumerator();
changeStream.MoveNext();
var next = changeStream.Current;
Then I downloaded the C# source code from MongoDB to see how they did this. Looking at their test code for change stream watches, they create a new document(Insert) and then change that document right away(Update) and THEN set up the Change Stream watch to receive an 'update' notification.
No example is given on how to watch for 'insert' notifications.
I have looked at the Java and NodeJS examples, both on MongoDB website and SO, which seems to be straight forward and defines a way to see both Inserts and Updates:
var changeStream = collection.watch({ '$match': { $or: [ { 'operationType': 'insert' }, { 'operationType': 'update' } ] } });
The API for the C# driver is vastly different, I would have assumed they would have kept the same API for C# as Java and NodeJS. I found no or very few examples for C# to do the same thing.
The closest I have come is with the following attempt but still fails and the documentation for the C# version is very limited (or I have not found the right location). Setup is as follows:
String json = "{ '$match': { 'operationType': { '$in': ['insert', 'update'] } } }";
var options = new ChangeStreamOptions { FullDocument = ChangeStreamFullDocumentOption.UpdateLookup };
PipelineDefinition<ChangeStreamDocument<BsonDocument>, ChangeStreamDocument<BsonDocument>> pipeline = new EmptyPipelineDefinition<ChangeStreamDocument<BsonDocument>>().Match(Builders<ChangeStreamDocument<BsonDocument>>.Filter.Text(json,"json"));
Then running the statement below throws an Exception:
{"Command aggregate failed: $match with $text is only allowed as the
first pipeline stage."}
No other Filter options has worked either, and I have not found a way to just enter the JSON as a string to set the 'operationType'.
var changeStream = collection.Watch(pipeline, options).ToEnumerable().GetEnumerator();
changeStream.MoveNext();
var next = changeStream.Current;
My only goal here is to be able to set the 'operationType' using the C# driver. Does anyone know what I am doing wrong or have tried this using the C# driver and had success?
After reading though a large number of webpages, with very little info on the C# version of the MongoDB driver, I am very stuck!
Any help would be much appreciated.
Here is a sample of code I've used to update the collection Watch to retrieve "events" other than just document updates.
IMongoDatabase sandboxDB = mongoClient.GetDatabase("Sandbox");
IMongoCollection<BsonDocument> collection = sandboxDB.GetCollection<BsonDocument>("TestCollection");
//Get the whole document instead of just the changed portion
ChangeStreamOptions options = new ChangeStreamOptions() { FullDocument = ChangeStreamFullDocumentOption.UpdateLookup };
//The operationType can be one of the following: insert, update, replace, delete, invalidate
var pipeline = new EmptyPipelineDefinition<ChangeStreamDocument<BsonDocument>>().Match("{ operationType: { $in: [ 'replace', 'insert', 'update' ] } }");
var changeStream = collection.Watch(pipeline, options).ToEnumerable().GetEnumerator();
changeStream.MoveNext(); //Blocks until a document is replaced, inserted or updated in the TestCollection
ChangeStreamDocument<BsonDocument> next = changeStream.Current;
enumerator.Dispose();
The EmptyPiplineDefinition...Match() argument could also be:
"{ $or: [ {operationType: 'replace' }, { operationType: 'insert' }, { operationType: 'update' } ] }"
If you wanted to use the $or command, or
"{ operationType: /^[^d]/ }"
to throw a little regex in there. This last one is saying, I want all operationTypes unless they start with the letter 'd'.

Parsing InfluxDB result using JSON.net

I'm trying to make a command line utility for initializing a InfluxDB database, but I'm pretty new to influx, and C# in general.
With the following response from the Influx DB Database, I'm trying to pretty print this in the console window.
Ideally I would have errors show up in the standard error buffer, and warnings or info's show up in the standard output.
However, when running the code below in a debug environment, messages appears to be in an incorrect format according to several jsonpath checkers that I have used.
JSON input as result.Body
{
"results": [
{
"statement_id": 0,
"messages": [
{
"level": "warning",
"text": "deprecated use of 'CREATE RETENTION POLICY Primary ON SensorData DURATION 30d REPLICATION 1' in a read only context, please use a POST request instead"
}
]
}
]
}
JSON output as messages prior to transformation:
messages {{
"level": "warning",
"text": "deprecated use of 'CREATE RETENTION POLICY Primary ON SensorData DURATION 30d REPLICATION 1' in a read only context, please use a POST request instead"
}} Newtonsoft.Json.Linq.JToken {Newtonsoft.Json.Linq.JObject}
As you can see, the messages output is in a nested object {{}} rather then an array as expected...
According to https://jsonpath.curiousconcept.com/ and several other jsonpath checkers, I was expecting something similar to:
[
{
"level":"warning",
"text":"deprecated use of 'CREATE RETENTION POLICY Primary ON SensorData DURATION 30d REPLICATION 1' in a read only context, please use a POST request instead"
}
]
C#
private static void PrintResult(IInfluxDataApiResponse result)
{
var output = result.Success ? System.Console.Out : System.Console.Error;
output.WriteLine("["+result.StatusCode + "] : "+result.Body);
var json = JObject.Parse(result.Body);
var messages = json.SelectToken("$.results[*].messages[*]"); //outputs an array of messages if exists. e.g. [{level:warning,text:test}]
if (messages != null)
{
var transformed = messages.Select(m => new { level = (string)m["level"], text = (string)m["text]"] }).ToList();
foreach (var msg in transformed)
{
output.WriteLine($"[{result.StatusCode}] : {msg.level} - {msg.text}");
}
}
}
For my uses at least, using var messages =
json.SelectTokens("$.results[*].messages[*]");
rather then
json.SelectToken("$.results[*].messages[*]");
allowed me to workaround the issue, as I could then treat the result as a C# enumerable, as opposed to special casing 1 result vs many results for SelectToken as it seems to flatten single results into an object, where as other implementations would have it be an array.

Create edit link to document in sharepoint

I have a document library in sharepoint storing a word document.
If I click on the link to the document I get a dialog box with "you want to open this file in readonly or editmode etc" and can open it in edit mode, change it, save it directly in word an the changes are saved in the document library.
The link to the file in the document library looks like this:
<a onfocus="OnLink(this)"
href="/test/DocLib2/wordtest.docx"
onmousedown="return VerifyHref(this,event,'1','SharePoint.OpenDocuments','')"
onclick="return DispEx(this,event,'TRUE','FALSE','FALSE',
'SharePoint.OpenDocuments.3','1', 'SharePoint.OpenDocuments',
'','','','1','0','0','0x7fffffffffffffff','','')"
>wordtest</a>
How do I create this link in my own web part where I have the name of the file and document library? Without just copying the above code, that wouldn't be a good idea...
Is there some "official" method to achieve this?
Unfortunately it doesn't seem like there is a better option. But at least you can sort of figure out what the function definition is. The DispEx function is defined in the core.js file (but it's easier to read in the core.debug.js). Both are in 14\Templates\Layouts\1033 directory.
Here is the function definition:
function DispEx(ele, objEvent, fTransformServiceOn, fShouldTransformExtension,
fTransformHandleUrl, strHtmlTrProgId, iDefaultItemOpen, strProgId, strHtmlType,
strServerFileRedirect, strCheckoutUser, strCurrentUser, strRequireCheckout,
strCheckedoutTolocal, strPermmask)
Here is my guess on what they mean. Please feel free to add comments to correct any mistakes or omissions:
ele - [obj] the element
objEvent - [obj] the event object
fTransformServiceOn - [bool] (unknown functionality) defaults to True
fShouldTransformExtension - [bool] (unknown functionality) defaults to False
fTransformHandleUrl - [bool] (unknown functionality) defaults to False
strHtmlTrProgId - [string] name of the ActiveXControl to try to load defaults to SharePoint.OpenDocuments.3
iDefaultItemOpen - [int] indicator of default to Edit or Read defaults to 1
strProgId - [string] name of the ActiveX Control
strHtmlType [string] (unknown functionality) defaults to empty
strServerFileRedirect - [string] (unknown functionality)
strCheckoutUser [string] the ID of the user who has checked out the document
strCurrentUser - [string] the ID of the current user
strRequireCheckout - [string] indicator whether to force a checkout
strCheckedoutTolocal - [string] indicator of whether to use the Local Drafts folder
strPermmask - [string] permissions mask for the current user defaults to 0x7fffffffffffffff
There are clearly some inconsistencies in terms of using strings and integers to represent boolean values. It's also strange that your code has 17 parameters but I can only find a function definition with 15 parameters, so I'm not sure what those last two empty strings are for. Some of that is the nature of JavaScript, but it also just looks sloppy on the part of Microsoft.
This doesn't really answer the question, hopefully it helps you or someone else.
Chad Schroeder made a blog post on how to construct the javascript function call in C#. Taking into account a couple of settings, like force checkout and open in browser or client for instance.
private string GetFileViewScript(SPFile file)
{
string text = SPUtility.MapToControl(SPContext.Current.Web, file.Name, string.Empty);
string text2 = (file.Item.ParentList.DefaultItemOpen == DefaultItemOpen.Browser) ? "1" : "0";
SPFieldLookupValue sPFieldLookupValue = file.Item["CheckedOutUserId"] as SPFieldLookupValue;
string scriptLiteralToEncode = (sPFieldLookupValue == null) ? string.Empty : sPFieldLookupValue.LookupValue;
string text3 = (SPContext.Current.Web.CurrentUser != null) ? SPContext.Current.Web.CurrentUser.ID.ToString(CultureInfo.InvariantCulture) : string.Empty;
string text4 = file.Item.ParentList.ForceCheckout ? "1" : "0";
return string.Format(CultureInfo.InvariantCulture, "return DispEx(this,event,'{0}','{1}','{2}','{3}','{4}','{5}','{6}','{7}','{8}','{9}','{10}','{11}','{12}')", new object[]
{
"TRUE",
"FALSE",
"FALSE",
text,
text2,
text,
string.Empty,
string.Empty,
SPHttpUtility.EcmaScriptStringLiteralEncode(scriptLiteralToEncode),
text3,
text4,
(string)file.Item["IsCheckedoutToLocal"],
(string)file.Item["PermMask"]
});
}
Using DispEx in a link to a SharePoint document
I end up with adding this code
return DispEx(this,event,'TRUE','FALSE','FALSE',
'SharePoint.OpenDocuments.3','1', 'SharePoint.OpenDocuments','','','',
'1','0','0','0x7fffffffffffffff','','')
to my link tag because I wasn't able to find a better solution.
If there is any, please let me know.
DispEx does not work in Chrome unless the link is within a div that contains the document type in an attribute called app:
<div class="ms-vb itx" ctxname="ctx19" id="2" app="ms-word">
<a onfocus="OnLink(this)"
href="/test/DocLib2/wordtest.docx"
onmousedown="return VerifyHref(this,event,'1','SharePoint.OpenDocuments','')"
onclick="return DispEx(this,event,'TRUE','FALSE','FALSE',
'SharePoint.OpenDocuments.3','1', 'SharePoint.OpenDocuments',
'','','','1','0','0','0x7fffffffffffffff','','')">wordtest</a>
<span class="ms-newdocument-iconouter">
<img class="ms-newdocument-icon" src="/_layouts/15/images/spcommon.png?rev=23" alt="new" title="new">
</span>
</div>
Either you need to wrap it in such a div, and be sure to insert the correct application that will open the file, or make your own list by looking at the file extension:
$('.test_links').click(function(e) {
e.preventDefault();
if (!!window.chrome) {
var extenstion = this.href.substr(this.href.lastIndexOf('.') + 1);
var prefix = '';
switch (extenstion) {
case 'doc':
case 'docx':
prefix = 'ms-word:ofv|u|';
break;
case 'xls':
case 'xlsx':
prefix = 'ms-excel:ofv|u|';
break;
}
window.location.href = prefix + this.href;
} else {
DispEx(this, e, 'TRUE', 'FALSE', 'FALSE', 'SharePoint.OpenDocuments.3', '0', 'SharePoint.OpenDocuments', '', '', '', _spPageContextInfo.userId + '', '0', '0', '0x7fffffffffffffff');
}
});
I don't remember if there is an official ability to do it with JavaScript COM, but you can use the ASP.NET HyperLink control to generate the similar link. For instance put in layout
<asp:HyperLink ID="EditHl" runat="server" Text="Edit document"/>
and in code-behind something like
EditHl.Attributes["attribute name"] = "attribute value";
just use the same values from OOTB link, but change
/test/DocLib2/wordtest.docx
to URL of your document.

Recursion limit exceeded

I am using a jQuery Ajax control from this site http://abeautifulsite.net/2008/03/jquery-file-tree/
I have it all working. I tried to ask a support question but never heard back, thinking maybe someone on here can shed some light on the situation.
Basically what I am trying to do, is on a file selection run an action that returns a JsonResult, that gives more details about the file and then show them to the screen in a container. While I debug, the method gets hit, returns the correct data. After the return in the ajax call i get a error in firebug say the recursionlimit exceeded. I am not sure how to get around this...I thought I could use the callback of the fileTree(options, callback) method provided but that is not fired on selection of the file only the initialization of the file tree. Any ideas?
Heres what I did via JavaScript
function initFileTree() {
$('#fileTree').fileTree({ root: '/', script: '/Scripts/filetree/jqueryFileTree.aspx', multiFolder: false, expandEasing: 'easeOutBounce', collapseEasing: 'easeOutBounce' }, function(file) {
getFileDetails(file);
});
}
function getFileDetails(file) {
// alert(file);
$.getJSON('/Files.mvc/GetFileDetails', { Data: file }, function(data) {
$('#fileDetail').html('<h6>Selected File: ' + data.Length + '</h6>');
}, 'json');
}
Here is my action that take the data and returns a JsonResult
public virtual JsonResult GetFileDetails(string data)
{
string pageMessage = null;
FileInfo fileInfo = null;
try
{
fileInfo = new FileInfo(data);
}
catch (Exception e)
{
pageMessage = e.Message;
}
return Json(fileInfo);
}
Apparently returning a FileInfo obj is not acceptable for a JsonResult. Simplifying the return, I changed it to:
return Json("helloWorld");
and all my problems went away. Not sure why it cares that I was attempting to return a FileInfo type but either way problem solved when I changed it to return a string. So now I just create a small wrapper class to hold the data I want to pass back and life is good.
Thanks! Hope this helps someone else.

Categories

Resources