I have the following test code which just creates an empty hashmap (containers.map) and fills it up afterwards:
hashtable = containers.Map('KeyType','char','ValueType','double');
hashtable('test') = 1.0;
as long as I use it in my "main" file, everything works fine...
But if I copy these 2 lines into a member function of an self created class, there comes the error:
error: class not found: MyClassName
error: called from
myMemberFunction at line 15 column 31
line 15 is the line where "hashtable('test') = 1.0;" stands.
If I remove this one line, everything works fine again.
I absolutly don't understand, why the error sais, it can not find my class...
I tested my class and everything works perfectly.
I set up the class in the following way:
1: created a folder named: "#MyClassName"
2: created a constructor file named: "MyClassName"
3: added a constructor function named: "function obj = MyClassName(obj)"
4: created a member function file named: "myMemberFunction"
5: created a member function within this file: "function obj = myMemberFunction(obj)
If this code (the hashmap) stands within my constructor it works fine.
Only if it stands inside my memberfunction line 15 "hashtable('test') = 1.0;" makes the error.
tl;dr - replace
hashtable('test') = 1.0;
with
subsasgn( hashtable, substruct( '()', {'test'} ), 1.0 );
Not sure if this is intended behaviour, or a side-effect of containers.Map being a classdef defined class, and whether or not this is because old and new style classes don't play well with each other. May be worth submitting a bug for this. But, seems like the problem is because hashtable('test') is not parsed as intended inside the method - presumably instead of validly treating it as a 'referencing' operation, it treats it as a function call?
In the interest of completeness, here's an MVCE:
% #MyClassName/MyClassName.m
function obj = MyClassName(obj)
obj = struct('myfield', []);
obj = class(obj, 'MyClassName');
end
% #MyClassName/display.m
function display( Obj );
display( [inputname(1), '.myfield = '] );
display( get( Obj, 'myfield' ) );
end
% #MyClassName/get.m
function Out = get( Obj, Field )
Out = struct(Obj).(Field);
end
% #MyClassName/set.m
function Obj = set( Obj, Field, Val )
Obj = struct(Obj);
Obj.(Field) = Val;
Obj = class( Obj, 'MyClassName' );
end
% #MyClassName/myMemberFunction.m
function obj = myMemberFunction(obj)
hashtable = containers.Map('KeyType', 'char', 'ValueType', 'double');
subsasgn( hashtable, substruct( '()', {'test'} ), 1.0 );
obj = set( obj, 'myfield', hashtable );
end
In terminal:
octave:1> a = MyClassName();
octave:2> a = myMemberFunction(a);
octave:3> a
a.myfield =
containers.Map object with properties:
Count : 1
KeyType : char
ValueType : double
Related
I am writing a web-application and am deserializing via jsony into norm-model-object types.
Norm-model-types are always ref objects. Somehow my code which is very similar to the default example in jsony's github documentation does not compile. Instead I receive the error SIGSEGV: Illegal storage access. (Attempt to read from nil?).
See here my code sample
import std/[typetraits, times]
import norm/[pragmas, model]
import jsony
const OUTPUT_TIME_FORMAT* = "yyyy-MM-dd'T'HH:mm:ss'.'ffffff'Z'"
type Character* {.tableName: "wikientries_character".} = ref object of Model
name*: string
creation_datetime*: DateTime
update_datetime*: DateTime
proc parseHook*(s: string, i: var int, v: var DateTime) =
##[ jsony-hook that is automatically called to convert a json-string to datetime
``s``: The full JSON string that needs to be serialized. Your type may only be a part of this
``i``: The index on the JSON string where the next section of it starts that needs to be serialized here
``v``: The variable to fill with a proper value]##
var str: string
s.parseHook(i, str)
v = parse(s, OUTPUT_TIME_FORMAT, utc())
proc newHook*(entry: var Character) =
let currentDateTime: DateTime = now()
entry.creation_datetime = currentDateTime # <-- This line is listed as the reason for the sigsev
entry.update_datetime = currentDateTime
entry.name = ""
var input = """ {"name":"Test"} """
let c = input.fromJson(Character)
I don't understand what the issue appears to be here, as the jsony-example on its github page looks pretty similar:
type
Foo5 = object
visible: string
id: string
proc newHook*(foo: var Foo5) =
# Populates the object before its fully deserialized.
foo.visible = "yes"
var s = """{"id":"123"}"""
var v = s.fromJson(Foo5)
doAssert v.id == "123"
doAssert v.visible == "yes"
How can I fix this?
The answer lies in the fact that norm-object-types are ref objects, not normal (value) objects (Thanks to ElegantBeef, Rika and Yardanico from the nim-discord to point this out)! If you do not explicitly 'create' a ref-type at one point, the memory for it is never allocated since the code doesn't do the memory allocation for you unlike with value types!
Therefore, you must initialize/create a ref-object first before you can use it, and Jsony does not take over initialization for you!
The correct way to write the above newHook thus looks like this:
proc newHook*(entry: var Character) =
entry = new(Character)
let currentDateTime: DateTime = now()
entry.creation_datetime = currentDateTime
entry.update_datetime = currentDateTime
entry.name = ""
When I execute this code (Windows 10) i get an error from within the library.
local json = loadfile("json.lua")()
local handle = io.popen("curl \"https://someurl.com\"")
local result = handle:read("*all")
handle:close()
local output = json:decode(result)
The error in the console:
lua: json.lua:377: expected argument of type string, got table
stack traceback:
[C]: in function 'error'
json.lua:377: in method 'decode'
monitor.lua:10: in main chunk
[C]: in ?
I'm running the code on Windows 10 with a console and using this library: https://github.com/rxi/json.lua
This function always returns the same error, even if I try different types of arguments, i.e. numbers or strings.
function json.decode(str)
if type(str) ~= "string" then
error("expected argument of type string, got " .. type(str))
end
local res, idx = parse(str, next_char(str, 1, space_chars, true))
idx = next_char(str, idx, space_chars, true)
if idx <= #str then
decode_error(str, idx, "trailing garbage")
end
return res
end
local output = json:decode(result)
is syntactic sugar for
local output = json.decode(json, result)
json is a table.
Hence inside function json.decode the following if statement is entered:
if type(str) ~= "string" then
error("expected argument of type string, got " .. type(str))
end
which produces the observed error.
To fix this you can either change the function definiton to
function json:decode(str)
-- code
end
Or you call
local output = json.decode(result)
You should pick the second one as changing the json library will affect code that already uses json.decode as intended by the author.
I am parsing a heavily nested JSON in VBA, using scriptcontrol/jscript.
The resulting JSON object is super nested, and has recurring 'useless' levels / layers called 'buckets'.
Is there a way I can remove these collectively from either my json string or the parsed json object?
Imagine it something like this:
responses.0.buckets.0.aggregations.0.10.buckets.0.5.buckets.0.9.buckets.0.20.buckets.0.8.buckets.0.13.buckets.0.14.buckets.0.15.buckets.0.16.buckets.0.19.buckets.0.18.buckets.0.21.doc_count_error_upper_bound
I'd only need the 'doc_count_error_upper_bound' value, and could essentially do without all the 0s and without all the buckets, making it less nested into:
responses.aggregations.10.5.9.20.8.13.14.15.16.19.18.21.doc_count_error_upper_bound
This would still be pretty heavily nested, but saves me a lot of headaches already.
I just do not know how I could do this with jscript/scriptcontrol in VBA (es3).
The source data is coming from a Kibana dashboard (examples on http://demo.elastic.co/ )
Thanks for any help!
Jasper
UPDATE:
Question regarding VBA code - the VBA code I have is irrelevant, as it's the standard way of loading a json string into an object via scriptcontrol.
I do not use EVAL, but for example purposes, it would be something like the below:
Dim Scr as Object, Json as Object
Set Scr = CreateObject("Scriptcontrol")
Scr.Language = "Jscript"
Set Json = Scr.Eval("(" & WinHTTP.ResponseText & ")")
I cannot share an example of the JSON string, as it contains sensitive data.
But ultimately, that's beside the question.
Consider example https://adobe.github.io/Spry/data/json/donuts.js
On the top there, is "batter" as key in between "batters" and the different IDs. If I'd want to remove that key, but keep the underlying ID data - how would I do that, through a js scrip that works in scriptcontrol in VBA?
UPDATE:
omegastripes answer worked very well, however, I failed to realize that a number of the keys I wanted to remove (the 'buckets' and '0' etc) had keys and values under them.
Let's take the example of the donuts, just altered a bit - see here:
https://pastebin.com/WxYir7vK
now I would want to remove the '0', '1', '2' etc keys without losing the underlying sub-keys.
However, for omegastripes code to work, I'd have to delete keys 'sequence', 'variant', 'name', and 'ppu' from all layers / throughout the json.
I can do that for one of them, for one layer with the function below:
function unseat(obj, prop) { for(var k in obj[prop]) obj[k] = obj[prop][k]; delete obj[prop]; return obj; }
And then calling the functio 'unseat (JSONObj, "variant")' - this works, but only for one of the four variables at a time and only for one layer.
How can I alter this so that I can remove it throughout the object, for all four at once, so that afterwards I can use omegastripes code to unwrap.
Summary
1) I take this json string: https://pastebin.com/WxYir7vK
2) parse it into script control into VBA
3) loop through it and remove all 'sequence', 'variant', 'name' and 'ppu' key/value pairs
4) unwrap it via omegastripes code.
Step 1 / 2 and 4 are taken care of - but how to do 3?
Thanks!
Using ScriptControl for parsing JSON has the following shortcomings (check this answer for details):
System environment is exposed to malware code injections received within response.
ScriptControl is not available on 64-bit MS Office.
Anyway if you are confident that operating in JScript environment is the only way, you may unwrap excessive nesting of objects and arrays structure using the below functions:
function gParse(sample) {
return eval('(' + sample + ')');
};
function gUnwrap(sample) {
for (var key in sample) {
sample[key] = gUnwrap(sample[key]);
};
var count = 0;
for (var key in sample) {
count++;
if (count == 2) break;
};
if (count == 1) {
var type = gGetType(sample);
if (type == 'Array' || type == 'Object') {
var type = gGetType(sample[key]);
if (type == 'Array' || type == 'Object') {
return sample[key];
}
}
};
return sample;
};
function gGetType(sample) {
return {}.toString.call(sample).slice(8, -1);
};
That could be done in VBA as shown below:
Option Explicit
Sub Test()
Dim sJSON As String
Dim ParseJSON As Object
Dim UnwrapJSON As Object
Dim oJSON As Object
With CreateObject("MSXML2.XMLHTTP")
.Open "GET", "https://adobe.github.io/Spry/data/json/donuts.js", False
.send
sJSON = .responseText
End With
With CreateObject("htmlfile")
With .parentWindow
.execScript "function gParse(sample) {return eval('(' + sample + ')')};"
.execScript "function gUnwrap(sample) {for (var key in sample) {sample[key] = gUnwrap(sample[key]);}; var count = 0; for (var key in sample) {count++; if (count == 2) break;}; if (count == 1) {var type = gGetType(sample); if (type == 'Array' || type == 'Object') {var type = gGetType(sample[key]); if (type == 'Array' || type == 'Object') {return sample[key];}}}; return sample;};"
.execScript "function gGetType(sample) {return {}.toString.call(sample).slice(8, -1)};"
Set ParseJSON = .gParse
Set UnwrapJSON = .gUnwrap
End With
End With
Set oJSON = UnwrapJSON(ParseJSON(sJSON))
End Sub
The locals window shows JSON object for the sample you provided as follows:
And unwrapped JSON object:
I am trying to figure out the issue, and tried different styles that I have read on Scala, but none of them work. My code is:
....
val str = "(and x y)";
def stringParse ( exp: String, pos: Int, expreshHolder: ArrayBuffer[String], follow: Int )
var b = pos; //position of where in the expression String I am currently in
val temp = expreshHolder; //holder of expressions without parens
var arrayCounter = follow; //just counts to make sure an empty spot in the array is there to put in the strings
if(exp(b) == '(') {
b = b + 1;
while(exp(b) == ' '){b = b + 1} //point of this is to just skip any spaces between paren and start of expression type
if(exp(b) == 'a') {
temp(arrayCounter) = exp(b).toString;
b = b+1;
temp(arrayCounter)+exp(b).toString; b = b+1;
temp(arrayCounter) + exp(b).toString; arrayCounter+=1}
temp;
}
}
val hold: ArrayBuffer[String] = stringParse(str, 0, new ArrayBuffer[String], 0);
for(test <- hold) println(test);
My error is:
Driver.scala:35: error: type mismatch;
found : Unit
required: scala.collection.mutable.ArrayBuffer[String]
ho = stringParse(str, 0, ho, 0);
^one error found
When I add an equals sign after the arguments in the method declaration, like so:
def stringParse ( exp: String, pos: Int, expreshHolder: ArrayBuffer[String], follow: Int ) ={....}
It changes it to "Any". I am confused on how this works. Any ideas? Much appreciated.
Here's a more general answer on how one may approach such problems:
It happens sometimes that you write a function and in your head assume it returns type X, but somewhere down the road the compiler disagrees. This almost always happens when the function has just been written, so while the compiler doesn't give you the actual source (it points to the line where your function is called instead) you normally know that your function's return type is the problem.
If you do not see the type problem straight away, there is the simple trick to explicitly type your function. For example, if you thought your function should have returned Int, but somehow the compiler says it found a Unit, it helps to add : Int to your function. This way, you help the compiler to help you, as it will spot the exact place, where a path in your function returns a non-Int value, which is the actual problem you were looking for in the first place.
You have to add the equals sign if you want to return a value. Now, the reason that your function's return value is Any is that you have 2 control paths, each returning a value of a different type - 1 is when the if's condition is met (and the return value will be temp) and the other is when if's condition isn't (and the return value will be b=b+1, or b after it's incremented).
class Test(condition: Boolean) {
def mixed = condition match {
case true => "Hi"
case false => 100
}
def same = condition match {
case true => List(1,2,3)
case false => List(4,5,6)
}
case class Foo(x: Int)
case class Bar(x: Int)
def parent = condition match {
case true => Foo(1)
case false => Bar(1)
}
}
val test = new Test(true)
test.mixed // type: Any
test.same // type List[Int]
test.parent // type is Product, the case class super type
The compiler will do its best to apply the most specific type it can based on the possible set of result types returned from the conditional (match, if/else, fold, etc.).
I am looking for a more pleasant way to add an argument name while calling a function.
Something which is better than either of these
local ret = foo( --[[argNam1 =]] true)
local ret = foo( true ) -- first argument is argName1
I tried
local ret = foo( argNam1 = true)
but I got error
')' expected near '='
You could use named parameters, if that's what you want. Lua tables are good for imitating that behavior. So instead of passing all parameters separated by a comma, you pass a single table object; that has named keys.
If that is your foo() function:
local foo(parameters)
print(parameters.argNam1)
end
Then you could call it like: local ret = foo{argNam1 = true}
Or call it like this:
local arguments = {
argNam1 = true,
argNam2 = "foobar"
}
local ret = foo(arguments)