How convert JSON to CoffeeScript and write on a file ".coffee" with NodeJS?
JSON:
{
"name": "jack",
"type": 1
}
to CoffeeScript:
"name": "jack"
"type": 1
Should be easy enough by traversing the object (for … of). Just use recursion and take the indent level as an argument:
esc_string = (s) ->
return '"' + s.replace(/[\\"]/g, '\\$1') + '"'
csonify = (obj, indent) ->
indent = if indent then indent + 1 else 1
prefix = Array(indent).join "\t"
return prefix + esc_string obj if typeof obj is 'string'
return prefix + obj if typeof obj isnt 'object'
return prefix + '[\n' + (csonify(value, indent) for value in obj).join('\n') + '\n' + prefix + ']' if Array.isArray obj
return (prefix + esc_string(key) + ':\n' + csonify(value, indent) for key, value of obj).join '\n'
Test case:
alert csonify
brother:
name: "Max"
age: 11
toys: [
"Lego"
"PSP"
]
sister:
name: "Ida"
age: 9
Result:
"brother":
"name":
"Max"
"age":
11
"toys":
[
"Lego"
"PSP"
]
"sister":
"name":
"Ida"
"age":
9
No live demo, since I don't know a JSFiddle for CoffeScript.
Live demo: http://jsfiddle.net/vtX3p/
I hope you know how to read and write files in nodejs, so i will not address that here.
To convert javascript to coffeescript you can use this npm:
https://github.com/rstacruz/js2coffee
Related
This question already has answers here:
Efficient read and write CSV in Go
(3 answers)
Closed 3 years ago.
I have a CSV file that uses a space as the delimiter. But some of the fields contain a space and those fields are wrapped with double quotes. Any field with a null/empty value is represented as "-". Fields that are not null/empty and do not contain spaces are not wrapped in double quotes. Here's an example of one row in the CSV file.
foobar "foo bar" "-" "-" "-" fizzbuzz "fizz buzz" fizz buzz
Also there are no headers for the CSV file. I was going to use a simple solution such as this one https://stackoverflow.com/a/20769342/3299397 but using strings.Split(csvInput, " ") wouldn't handle the spaces inside the fields. I've also looked into this library https://github.com/gocarina/gocsv but I'm curious if there's a solution that doesn't use a third-party library.
This is "plain" CSV format where the separator is the space character instead of comma or semicolon. The encoding/csv package can handle this.
As to your null / empty fields: just use a loop as a post-processing step and replace them with the empty string.
Using the input:
const input = `foobar "foo bar" "-" "-" "-" fizzbuzz "fizz buzz" fizz buzz
f2 "fo ba" "-" "-" "-" fd "f b" f b`
Parsing and post-processing it:
r := csv.NewReader(strings.NewReader(input))
r.Comma = ' '
records, err := r.ReadAll()
if err != nil {
panic(err)
}
fmt.Printf("%#v\n", records)
for _, r := range records {
for i, v := range r {
if v == "-" {
r[i] = ""
}
}
}
fmt.Printf("%#v\n", records)
Output (try it on the Go Playground):
[][]string{[]string{"foobar", "foo bar", "-", "-", "-", "fizzbuzz", "fizz buzz", "fizz", "buzz"}, []string{"f2", "fo ba", "-", "-", "-", "fd", "f b", "f", "b"}}
[][]string{[]string{"foobar", "foo bar", "", "", "", "fizzbuzz", "fizz buzz", "fizz", "buzz"}, []string{"f2", "fo ba", "", "", "", "fd", "f b", "f", "b"}}
This is the question about how to parse "unparseable" output into json, or to something easily consumable as json. This is "little" bit behind trivial stuff, so I'd like to know, how do you solve these things in principle, it's not about this specific example only. But example:
We have this command, which shows data about audio inputs:
pacmd list-sink-inputs
it prints something like this:
2 sink input(s) available.
index: 144
driver: <protocol-native.c>
flags:
state: RUNNING
sink: 4 <alsa_output.pci-0000_05_00.0.analog-stereo>
volume: front-left: 15728 / 24% / -37.19 dB, front-right: 15728 / 24% / -37.19 dB
balance 0.00
muted: no
current latency: 70.48 ms
requested latency: 210.00 ms
sample spec: float32le 2ch 44100Hz
channel map: front-left,front-right
Stereo
resample method: copy
module: 13
client: 245 <MPlayer>
properties:
media.name = "UNREAL! Tetris Theme on Violin and Guitar-TnDIRr9C83w.webm"
application.name = "MPlayer"
native-protocol.peer = "UNIX socket client"
native-protocol.version = "32"
application.process.id = "1543"
application.process.user = "mmucha"
application.process.host = "vbDesktop"
application.process.binary = "mplayer"
application.language = "C"
window.x11.display = ":0"
application.process.machine_id = "720184179caa46f0a3ce25156642f7a0"
application.process.session_id = "2"
module-stream-restore.id = "sink-input-by-application-name:MPlayer"
index: 145
driver: <protocol-native.c>
flags:
state: RUNNING
sink: 4 <alsa_output.pci-0000_05_00.0.analog-stereo>
volume: front-left: 24903 / 38% / -25.21 dB, front-right: 24903 / 38% / -25.21 dB
balance 0.00
muted: no
current latency: 70.50 ms
requested latency: 210.00 ms
sample spec: float32le 2ch 48000Hz
channel map: front-left,front-right
Stereo
resample method: speex-float-1
module: 13
client: 251 <MPlayer>
properties:
media.name = "Trombone Shorty At Age 13 - 2nd Line-k9YUi3UhEPQ.webm"
application.name = "MPlayer"
native-protocol.peer = "UNIX socket client"
native-protocol.version = "32"
application.process.id = "2831"
application.process.user = "mmucha"
application.process.host = "vbDesktop"
application.process.binary = "mplayer"
application.language = "C"
window.x11.display = ":0"
application.process.machine_id = "720184179caa46f0a3ce25156642f7a0"
application.process.session_id = "2"
module-stream-restore.id = "sink-input-by-application-name:MPlayer"
very nice. But we don't want to show user all of this, we just want to show index (id of input), application.process.id, application.name and media.name, in some reasonable format. It would be great to parse it somehow to json, but even if I preprocess it somehow, the jq is way beyond my capabilities and quite complex. I tried multiple approaches using jq, with regex or without, but I wasn't able to finish it. And I guess we cannot rely on order or presence of all fields.
I was able to get the work "done", but it's messy, inefficient, and namely expects no semicolons in media name or app name. Not acceptable solution, but this is the only thing I was able to bring to the "end".
incorrect solution:
cat exampleOf2Inputs |
grep -e "index: \|application.process.id = \|application.name = \|media.name = " |
sed "s/^[ \t]*//;s/^\([^=]*\) = /\1: /" |
tr "\n" ";" |
sed "s/$/\n/;s/index:/\nindex:/g" |
tail -n +2 |
while read A; do
index=$(echo $A|sed "s/^index: \([0-9]*\).*/\1/");
pid=$(echo $A|sed 's/^.*application\.process\.id: \"\([0-9]*\)\".*$/\1/');
appname=$(echo $A|sed 's/^.*application\.name: \"\([^;]*\)\".*$/\1/');
medianame=$(echo $A|sed 's/^.*media\.name: \"\([^;]*\)\".*$/\"\1\"/');
echo "pid=$pid index=$index appname=$appname medianame=$medianame";
done
~ I grepped the interessant part, replaced newlines with semicolon, split to multiple lines, and just extract the data multiple times using sed. Crazy.
Here the output is:
pid=1543 index=144 appname=MPlayer medianame="UNREAL! Tetris Theme on Violin and Guitar-TnDIRr9C83w.webm"
pid=2831 index=145 appname=MPlayer medianame="Trombone Shorty At Age 13 - 2nd Line-k9YUi3UhEPQ.webm"
which is easily convertable to any format, but the question was about json, so to:
[
{
"pid": 1543,
"index": 144,
"appname": "MPlayer",
"medianame": "UNREAL! Tetris Theme on Violin and Guitar-TnDIRr9C83w.webm"
},
{
"pid": 2831,
"index": 145,
"appname": "MPlayer",
"medianame": "Trombone Shorty At Age 13 - 2nd Line-k9YUi3UhEPQ.webm"
}
]
Now I'd like to see, please, how are these things done correctly.
If the input is as reasonable as shown in the Q, the following approach that only uses jq should be possible.
An invocation along the following lines is assumed:
jq -nR -f parse.jq input.txt
def parse:
def interpret:
if . == null then .
elif startswith("\"") and endswith("\"")
then .[1:-1]
else tonumber? // .
end;
(capture( "(?<key>[^\t:= ]*)(: | = )(?<value>.*)" ) // null)
| if . then .value = (.value | interpret) else . end
;
# Construct one object for each "segment"
def construct(s):
[ foreach (s, 0) as $kv (null;
if $kv == 0 or $kv.index
then .complete = .accumulator | .accumulator = $kv
else .complete = null | .accumulator += $kv
end;
.complete // empty ) ]
;
construct(inputs | parse | select(.) | {(.key):.value})
| map( {pid: .["application.process.id"],
index,
appname: .["application.name"],
medianame: .["media.name"]} )
With the example input, the output would be:
[
{
"pid": "1543",
"index": 144,
"appname": "MPlayer",
"medianame": "UNREAL! Tetris Theme on Violin and Guitar-TnDIRr9C83w.webm"
},
{
"pid": "2831",
"index": 145,
"appname": "MPlayer",
"medianame": "Trombone Shorty At Age 13 - 2nd Line-k9YUi3UhEPQ.webm"
}
]
Brief explanation
parse parses one line. It assumes that whitespace (blank and tab characters) on each line before the key name can be ignored.
construct is responsible for grouping the lines (presented as a stream of key-value single-key objects) corresponding to a particular value of “index”. It produces an array of objects, one for each value of “index”.
I don't know about "correctly", but this is what I'd do:
pacmd list-sink-inputs | awk '
BEGIN { print "[" }
function print_record() {
if (count++) {
print " {"
printf " %s,\n", print_number("pid")
printf " %s,\n", print_number("index")
printf " %s,\n", print_string("appname")
printf " %s\n", print_string("medianame")
print " },"
}
delete record
}
function print_number(key) { return sprintf("\"%s\": %d", key, record[key]) }
function print_string(key) { return sprintf("\"%s\": \"%s\"", key, record[key]) }
function get_quoted_value() {
if (match($0, /[^"]+"$/))
return substr($0, RSTART, RLENGTH-1)
else
return "?"
}
$1 == "index:" { print_record(); record["index"] = $2 }
$1 == "application.process.id" { record["pid"] = get_quoted_value() }
$1 == "application.name" { record["appname"] = get_quoted_value() }
$1 == "media.name" { record["medianame"] = get_quoted_value() }
END { print_record(); print "]" }
' |
tac | awk '/},$/ && !seen++ {sub(/,$/,"")} 1' | tac
where the tac|awk|tac line removes the trailing comma from the last JSON object in the list.
[
{
"pid": 1543,
"index": 144,
"appname": "MPlayer",
"medianame": "UNREAL! Tetris Theme on Violin and Guitar-TnDIRr9C83w.webm"
},
{
"pid": 2831,
"index": 145,
"appname": "MPlayer",
"medianame": "Trombone Shorty At Age 13 - 2nd Line-k9YUi3UhEPQ.webm"
}
]
You could just pipe your output into:
sed -E '
s/pid=([0-9]+) index=([0-9]+) appname=([^ ]+) medianame=(.*)/{"pid": \1, "index": \2, "appname": "\3", "medianame": \4},/
1s/^/[/
$s/,$/]/
' | jq .
I am struggling with that issue for a quite long time.
In fact, I have a QR Code in which I have this text:
{ "Version ": 0x1, "Type ": "MapPoint ", "X ": 2, "Y ": 3}
Then I transform the content of this QR code to a Json Object:
JSONObject scanQRCode = new JSONObject(contents);
When I debug, I find that the string contents is like that:
" { \"Version \": 0x1, \"Type \": \"MapPoint \", \"X \": 2, \"Y \": 3} "
And ScanQRCode equals to that:
{{"Version ":1,"Type ":"MapPoint ","X ":2,"Y ":3}} Org.Json.JSONObject
And the in my code I have to verify this condition and get the double X and Y as following:
if (scanQRCode.Has("Version") && scanQRCode.GetInt("Version") >= 0x1 && scanQRCode.Has(KEY_QR_TYPE) && scanQRCode.GetString("Type").Equals(("MapPoint")))
{
float x = (float)scanQRCode.GetDouble("X");
float y = (float)scanQRCode.GetDouble("Y");
}
the problem is that condition is never fulfilled and I can't even get for example the double X.
Can you please tell me what is wrong?
{ "Version": "0x1", "Type":"MapPoint", "X": 2, "Y": 3}
This is already a json object no need to convert it again you can directly use it as follow
var a = { "Version": "0x1", "Type":"MapPoint", "X": 2, "Y": 3}
float x = a.X;
I have this problem :
I recive strings with this format:
{
{
"name":"j1",
"type":"12"
},
{
"name":"j2",
"type":"15"
},
.....
}
I would like to read data
like get("name") and get(type) to read all data values and show like
j1 12
j2 15
j3 23 and so on
I am using RAD studio ,firemonkey for mobile devices.
I wrote this code to parse the string to JSON an read it .
jo: TJSONObject;
jp: TJSONPair;
va1:TJSONvalue;
va2:TJSONvalue;
jo:= TJSONObject.ParseJSONValue(TEncoding.ASCII.GetBytes(a),0) as TJSONObject;
jp := jo.Get(1);
for i := 0 to jo.Size - 1 do
begin
jp := jo.Get(i);
Memo1.Lines.Add('ToString: ' + jp.ToString);
Memo1.Lines.Add('JsonString: ' + jp.JsonString.Value);
if (jp.JSONValue is TJSONTrue) or
(jp.JSONValue is TJSONFalse) or
(jp.JSONValue is TJSONNull) then
Memo1.Lines.Add('JsonValue: ' + jp.JsonValue.ToString)
else
Memo1.Lines.Add('JsonValue: ' + jp.JsonValue.Value)
end;
When the program execute jp := jo.Get(1); I have this error "Access violation at adress 53605374 accessing address 0000013C"
I have tried other ways ... but I have not been able to solve the mystery.
According to json.org this is not a valid json.
A valid json (validated online ) would be:
[
{
"name":"j1",
"type":"12"
},
{
"name":"j2",
"type":"15"
},
.....
]
Or
{
data: [
{
"name":"j1",
"type":"12"
},
{
"name":"j2",
"type":"15"
},
.....
]
}
I am writing some helper functions to convert my R variables to JSON. I've come across this problem: I would like my values to be represented as JSON arrays, this can be done using the AsIs class according to the RJSONIO documentation.
x = "HELLO"
toJSON(list(x = I(x)), collapse="")
"{ \"x\": [ \"HELLO\" ] }"
But say we have a list
y = list(a = "HELLO", b = "WORLD")
toJSON(list(y = I(y)), collapse="")
"{ \"y\": {\n \"a\": \"HELLO\",\n\"b\": \"WORLD\" \n} }"
The value found in y -> a is NOT represented as an array. Ideally I would have
"{ \"y\": [{\n \"a\": \"HELLO\",\n\"b\": \"WORLD\" \n}] }"
Note the square brackets. Also I would like to get rid of all "\n"s, but collapse does not eliminate the line breaks in nested JSON. Any ideas?
try writing as
y = list(list(a = "HELLO", b = "WORLD"))
test<-toJSON(list(y = I(y)), collapse="")
when you write to file it appears as:
{ "y": [
{
"a": "HELLO",
"b": "WORLD"
}
] }
I guess you could remove the \n as
test<-gsub("\n","",test)
or use RJSON package
> rjson::toJSON(list(y = I(y)))
[1] "{\"y\":[{\"a\":\"HELLO\",\"b\":\"WORLD\"}]}"
The reason
> names(list(a = "HELLO", b = "WORLD"))
[1] "a" "b"
> names(list(list(a = "HELLO", b = "WORLD")))
NULL
examining the rjson::toJSON you will find this snippet of code
if (!is.null(names(x)))
return(toJSON(as.list(x)))
str = "["
so it would appear to need an unnamed list to treat it as a JSON array. Maybe RJSONIO is similar.