Is it possible to export/import json file in Calc with macro?

Guys, I’m thinking about creating a macro that lets you import and read json files written in language programming basic for Calc, I hope who can help us with this:

this code snippet saves the file in json, my question is how to read all the data from the spreadsheet and save it as a string in this file. is there a function in calc for this?

Sub exportCalcAsJSON()
Dim FilterNames(1,2) As String, sResult As String, sPath As String
    FilterNames(0,0) = "Json" : FilterNames(0,1) = "*.json" : FilterNames(0,2) = "calc8"
    sPath = ConvertToURL("C:\Users\username\folderName\")
    GlobalScope.BasicLibraries.LoadLibrary("Tools")
    sResult = StoreDocument(ThisComponent, FilterNames, "", sPath)
    If sResult = "" Then 
        MsgBox "File not saved", MB_ICONEXCLAMATION, "Error"
    Else 
        MsgBox "File saved as " + ConvertFromURL(sResult), MB_OK, "Success" 
    EndIf 
End Sub

refs

That code snippet does not save as JSON … it saves in the default ODF file format (calc8).

FilterNames(0,0) = "Json" just tells the UI filter name, FilterNames(0,1) = "*.json" the file mask to list files in the dialog, but FilterNames(0,2) = "calc8" is the actual filter used.

Btw, with Dim FilterNames(1,2) you unnecessarily declare an array with two rows but use only one. Array dimensions are declared with UpperBound, so that array is 0..1, 0..2

2 Likes

To export all data of a sheet you’d have to obtain the used area of a sheet and iterate over all cells to get their content and stuff it into your JSON structure (however you’d define that, first row cell content as column IDs or whatever). See Cursors and for example use cursor.gotoStartOfUsedArea(True) and cursor.gotoEndOfUsedArea(True) (not False in this case) to create a range selection that spans the used area, then cursor.gotoStart() and cursor.gotoNext() .

2 Likes

why BASIC … Python can be used also as Scripting-language in LO … and …surprise… Python has a Builtin-module named json which is exactly for your purpose.

https://docs.python.org/3/library/json.html

nobody can help without knowledge about how your data is organized in the sheet[s?] and how it should go into …json

2 Likes

In Basic you can use the sheet function getDataArray() and create the string from the array in some loop (for…next, do…while etc.). And then write the string to the file by the TextStream.

Sub saveStringToFile 'write the string to the file
	on local error goto bug
	dim sEncoding$, sFileName$, sUrl$,  oSFA as object, oStream as object, oTextStream as object, s$
	sFileName="d:/test.json" 'your file
	s="blablabla JSON" 'your string
	sEncoding="UTF-8" 'encoding
	sUrl=ConvertToUrl(sFileName)
	oSFA=CreateUnoService("com.sun.star.ucb.SimpleFileAccess")
	oTextStream=CreateUnoService("com.sun.star.io.TextOutputStream")
	oTextStream.setEncoding(sEncoding)
	if oSFA.exists(sFileName) then 'file exists so delete one
		oSFA.kill(sFileName)
	end if
	oStream=oSFA.openFileWrite(sUrl) 'open for write
	oTextStream.setOutputStream(oStream)
	oTextStream.writeString(s) 'write the string to the file
	oStream.closeOutput() : oTextStream.closeOutput() 'close outputs
	exit sub
bug:
	msgbox("line: " & Erl & chr(13) & Err & ": " & Error, 16, "saveStringToFile")
End Sub
basic
1 Like

@KamilLanda: …json is NOT simple plain text, your huba-duba-famous-Basic-code written in python:

def write_file():
    with open( "D:/test.json" , "w" , encoding="utf8") as output:
        output.write( "blablabla JSON" )

why did you show such a useless BASIC&Api-code (in context of writing …json)??

1 Like

I tried something like this

REM  *****  BASIC  *****
Sub Main
End Sub

'write the string to the file
Sub saveStringToFile
	on local error goto bug
	dim  FilterNames(1,2) As String, sResult As String, sPath As String, sEncoding$, namer$, indexFile$, endFile$, count$, sFileName$, sUrl$,  oSFA as object, oStream as object, oTextStream as object, s$
	
	sheet = ThisComponent.CurrentController.ActiveSheet

	'your file
	sFileName="e:/test.json"
	
	'your string
	count= "{ count: "+sheet.getCellRangeByName("D5:F10").Columns.Count
	namer= ", name: "+sheet.getCellRangeByName("D5:F10").Columns("E").Name
	endFile= ": }"
	indexFile= ", index: "+range.Columns(0).Name
	sEncoding="UTF-8"

	'encoding
	sUrl=ConvertToUrl(sFileName)

	oSFA=CreateUnoService("com.sun.star.ucb.SimpleFileAccess")
	oTextStream=CreateUnoService("com.sun.star.io.TextOutputStream")
	oTextStream.setEncoding(sEncoding)

	if oSFA.exists(sFileName) then 
		'file exists so delete one
		oSFA.kill(sFileName)
	end if

	'open for write
	oStream=oSFA.openFileWrite(sUrl)
	oTextStream.setOutputStream(oStream)

	'write the string to the file
	oTextStream.writeString(s)

	'close outputs
	oStream.closeOutput() : oTextStream.closeOutput()
	exit sub
bug:
	msgbox("line: " & Erl & chr(13) & Err & ": " & Error, 16, "saveStringToFile")
End Sub

but it gives an error, I tried to select all the rows and columns of the calc along with the name of these columns and the index too

I’m trying to use string concatenation in basic

I tried to do this and it gives an error

You can test this:
test-json.ods (14.7 kB)

But the big disadvantage is: you create manually the string you write to the JSON file. So if you want to use the strings and also the numbers in JSON file {“name”: “kamil”, “number” : 15} you have to do it in the for…next loop. There are only strings in my example.

@karolus: I don’t know the Python :-). And I haven’t the time to learn it. I don’t curse the Basic, I learned some Basic when I was child and started with some programming, so for me it is not hideous :-). I tried only some small things in the Python. In many things the syntax of the Python is easier, but usage of the Python in LibreOffice is “masochism” :-). But If you love Python, you exceed the disadvantages :-). For me the biggest disadvantage is no built-in editor like for the Basic.

@KamilLanda: Sorry our point of view seems different, for me it pure “masochism” to write things like your ~35 locs in BASIC:

option explicit

Sub testJSON 'write string from the cells to the JSON file
	dim oDoc as object, oSheet as object, oRange as object, data(), i%, s$
	oDoc=ThisComponent
	oSheet=oDoc.CurrentController.ActiveSheet
	oRange=oSheet.getCellRangeByName("A1:B4")
	data=oRange.getDataArray() 'array with the cell content
	s="{ "
	for i=lbound(data) to ubound(data) 'create the JSON string "manually"
		s=s & """" & data(i)(0) & """: " & """" & data(i)(1) & """"
		if i<>ubound(data) then
			s=s & ", "
		end if
	next i
	s=s & " }"
	if NOT saveStringToFile("d:/test.json", s, "UTF-8") then msgbox("Error: Write to the JSON file", 16, "testJSON")
End Sub

Function saveStringToFile(sFileName$, s$, optional sEncoding$) as boolean 'write the string to the file
	on local error goto bug
	if isMissing(sEncoding) then sEncoding="UTF-8" 'default encoding
	dim sUrl$,  oSFA as object, oStream as object, oTextStream as object
	sUrl=ConvertToUrl(sFileName)
	oSFA=CreateUnoService("com.sun.star.ucb.SimpleFileAccess")
	oTextStream=CreateUnoService("com.sun.star.io.TextOutputStream")
	oTextStream.setEncoding(sEncoding)
	if oSFA.exists(sFileName) then 'file exists so delete one
		oSFA.kill(sFileName)
	end if
	oStream=oSFA.openFileWrite(sUrl) 'open for write
	oTextStream.setOutputStream(oStream)
	oTextStream.writeString(s) 'write the string to the file
	oStream.closeOutput() : oTextStream.closeOutput() 'close outputs
	saveStringToFile=true
	exit function
bug:
	msgbox("line: " & Erl & chr(13) & Err & ": " & Error, 32, "saveStringToFile")
	saveStringToFile=false
End Function

The very same thing in Python:

import json

def save_to_dict():
    doc = XSCRIPTCONTEXT.getDocument()
    data = doc.CurrentController.ActiveSheet["A1:B4"].DataArray
    with open('test.json','w') as jsonfile:
        json.dump( dict(data), jsonfile )

and the content of test.json:

{"name": "kamil", "surname": "landa", "race": "human", "face": "human"}
2 Likes

How can I get any row and column of calc? Is there any command that does this?

@erAck has already pointed out, how to obtain any data:

but again: we need at least an Example-dokument which describes how the data is organized in Calc, and a unambigous description by you how this data should go into the …json-structure.

2 Likes

This example is good, but there is a problem because it is in series. And I don’t want to do a series, I want something like this:

example 1

import json

def save_to_dict():
    doc = XSCRIPTCONTEXT.getDocument()
    columnsCells = ["A1", "B4"] # range all columns and cells
     for x in columnsCells:
            data = doc.CurrentController.ActiveSheet[x[0]+x[1].DataArray
    with open('test.json','w') as jsonfile:
        json.dump( dict(data), jsonfile )

describe: in this small change, I can automatically add a series from A1 to B4. But I can also generate a for that generates these automatic indexes. It’s not so good my example, but it’s something I’m thinking folks.

example 2

import json

def save_to_dict():
    doc = XSCRIPTCONTEXT.getDocument()
    allColumnsCells = [list(range(1, 100))] # range all columns and cells
     for x in allColumnsCells:
            data = doc.CurrentController.ActiveSheet[x].DataArray
    with open('test.json','w') as jsonfile:
        json.dump( dict(data), jsonfile )

problem

my problem is that I have to know how many rows and columns there are, if I know how many rows and columns there are, the next step is to read the rows and columns, after that the next step is to generate the json file with this information
if I need to export that’s it, this is the sequence of steps,
if i need to read the file i would have to read it in json format and specify to the program to write this information in the new odt file with the rows and columns again

example 3

import json

def settingsFileUser():
   addFilepathODT = input("add filepath odt: ") # C:\Users\username\folderName\calc.json
   importFileJsonCalc(addFilepathODT )  # C:\Users\username\folderName\calc.json =: C:\Users\username\folderName\new_file_calc.odt
   save_to_dic_export_jsonCalc(addFilepathODT )  # C:\Users\username\folderName\calc.json

def save_to_dic_export_jsonCalc(addFilepathODT ):
    doc = XSCRIPTCONTEXT.getDocument()
    for sheet in doc.Sheets:
      allColumnsCells = [list(range(sheet.Column, sheet.Row))] # range all columns and cells
     for x in allColumnsCells:
            data = doc.CurrentController.ActiveSheet[x].DataArray
     sheetFileName = doc.getCurrentController().getActiveSheet()
    with open(sheetFileName+'.json','w') as jsonfile:
        json.dump( dict(data), jsonfile )

def importFileJsonCalc(): # read the file in json and create the corresponding odt file.
    doc = XSCRIPTCONTEXT.getDocument()
    for sheet in doc.Sheets:
      allColumnsCells = [list(range(sheet.Column, sheet.Row))] # range all columns and cells
     for x in allColumnsCells:
            data = doc.CurrentController.ActiveSheet[x].DataArray
     sheetFileName = doc.getCurrentController().getActiveSheet()
    with open(sheetFileName+'.json','w') as jsonfile:
        json.dump( dict(data), jsonfile )
    with open(sheetFileName+'.odt','w') as jsonfile:
        json.dump( dict(data), jsonfile )

refs

you dont need to teach me about numpy , range or iterating over sheets, also your non-working-phantasie-code doesnt provide any sensefull information.

I don’t know how to program, I’m trying. sorry I’m not criticizing you, I’m trying to work this out, trying to

"""
def exportFileJson():
    doc = XSCRIPTCONTEXT.getDocument()
    for sheet in doc.Sheets:
      allColumnsCells = [list(range(sheet.Column, sheet.Row))]
     for x in allColumnsCells:
            data = doc.CurrentController.ActiveSheet[x].DataArray
     sheetFileName = doc.getCurrentController().getActiveSheet()
    with open(sheetFileName+'.json','w') as jsonfile:
        json.dump( dict(data), jsonfile )
"""


def exportFileJson():
    print("export file Json ")

def importFileJson():
    doc = XSCRIPTCONTEXT.getDocument()
    for sheet in doc.Sheets:
      allColumnsCells = [list(range(sheet.Column, sheet.Row))]
      for x in allColumnsCells:
         data = doc.CurrentController.ActiveSheet[x].DataArray
    fileNameJson = input("filepath json: ") 
    with open(fileNameJson,'w') as jsonfile:
        json.dump( dict(data), jsonfile )

def menu():
    while True:
        print ("1) import file Json ")
        print ("2) export file Json ")
        choice = input('Enter your choice: ').lower()
        if choice == '1':
            importFileJson()
        elif choice == '2':
            exportFileJson()
        else:
            print(f'Not a correct choice: <{choice}>,try again')
 
 
if __name__ == '__main__':
    menu()

I give up on it… without any real Example.ods nor description… no chance!!

did you see my code?

I’m reading every row and column of the file with this line of code:

allColumnsCells = [list(range(sheet.Column, sheet.Row))]
# A1: n elements 

please…please…please attach an:

Example.ods

which gives a little clue about what you want

You can obtain the spanned range of a SheetCellCursor with cursor.RangeAddress, for illustration (the A1 is just to start somewhere, if data region begins right or below then gotoStartOfUsedArea(False) will move the top left position of the cursor range to there):

Sub Main
	sheet = ThisComponent.CurrentController.ActiveSheet
	cell = sheet.getCellRangeByName("A1")
	cursor = sheet.createCursorByRange(cell)
	cursor.gotoStartOfUsedArea(False)
	cursor.gotoEndOfUsedArea(True)
	print cursor.RangeAddress.StartRow, cursor.RangeAddress.EndRow, cursor.RangeAddress.EndRow - cursor.RangeAddress.StartRow + 1
	print cursor.RangeAddress.StartColumn, cursor.RangeAddress.EndColumn, cursor.RangeAddress.EndColumn - cursor.RangeAddress.StartColumn + 1
End Sub

See also the interface documentation,
XUsedAreaCursor
XCellCursor

2 Likes