Skip to content

Commit

Permalink
replace jsonc_parser
Browse files Browse the repository at this point in the history
  • Loading branch information
wyf9 committed Jul 30, 2024
1 parent cc2d9b1 commit f757197
Show file tree
Hide file tree
Showing 12 changed files with 398 additions and 4 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
__pycache__/
data.json
data.json
data.old.json
2 changes: 1 addition & 1 deletion data.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

def initJson():
try:
jsonData = jsonp.parse_file('example.jsonc')
jsonData = jsonp.parse_file('example.jsonc', encoding='utf-8')
with open('data.json', 'w+', encoding='utf-8') as file:
json.dump(jsonData, file, indent=4, ensure_ascii=False)
except:
Expand Down
20 changes: 20 additions & 0 deletions jsonc_parser/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Builder script
project_manifest.json
build.py

# __pycache__ folder
**/__pycache__

# Tools for docs
compile_docs.py
**/docs_templates

# Test files
test.py
test.json
test.jsonc

# Distribution folders
/build
/dist
/*.egg-info
21 changes: 21 additions & 0 deletions jsonc_parser/LICENSE.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2021 NJickolai Beloguzov

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
1 change: 1 addition & 0 deletions jsonc_parser/MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include README.md
85 changes: 85 additions & 0 deletions jsonc_parser/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
本文件夹中的内容来自 [kobayashi-deepinsight/jsonc-parser](https://github.com/kobayashi-deepinsight/jsonc-parser)

> 以下为原文件内容
# jsonc-parser

This package is a lightweight, zero-dependency module for parsing files with .jsonc extension. (<i>a.k.a. JSON with comments</i>)

## Installation

To install this package, simply download it from [PyPI](https://pypi.org/project/jsonc-parser):

pip install jsonc-parser

Also you can build it yourself from source code available on [GitHub](https://github.com/NickolaiBeloguzov/jsonc-parser)

## Usage

You need to just import _JsoncParser_ class from this package:

from jsonc_parser.parser import JsoncParser

This class requires no instance to function (i.e. it is fully static)

## Functions

These are all methods that JsoncParser class provides for working with .jsonc files:

- ##### JsoncParser.parse_file(filepath: PathLike) -> dict

This function parses file, specified in _filepath_ parameter, and deserializes it into a valid Python object (dictionary), removing any comment in the process. No alterations are made it the file itself. _filepath_ parameter specifies path to .jsonc file.

from jsonc_parser.parser import JsoncParser

file_path = "./data.jsonc"
# Content from 'data.jsonc' -> {"version": "1.0.0" /*This is my project's version*/}

data = JsoncParser.parse_file(file_path)

print(data)
# Output: {'version': '1.0.0'}

This function can raise _[FunctionParameterError](#exc-function-parameter-error)_ if filepath parameter is not a path-like object (str, bytes object representing a path, or os.PathLike compliant) or is empty. Also this function will raise _[FileError](#exc-file-error)_ exception if file's format is unsupported and a _[ParserError](#exc-parser-error)_ exception if file cannot be parsed/contains invalid JSON data.

- ##### JsoncParser.parse_str(_string: str) -> dict

This function parses string, specified in __string_ parameter, and deserializes it into a valid Python object (dictionary), removing any comment in the process.

from jsonc_parser.parser import JsoncParser

json_string = """{"version": "1.0.0" /*This is my project's version*/}"""

data = JsoncParser.parse_str(json_string)

print(data)
# Output: {'version': '1.0.0'}

This function can raise _[FunctionParameterError](#exc-function-parameter-error)_ if __string_ parameter is not a string or is empty. Also this function will raise a _[ParserError](#exc-parser-error)_ exception if file cannot be parsed/contains invalid JSON data.

##### JsoncParser.convert_to_json(filepath: PathLike, remove_file: bool = False) -> None
This function converts file from .jsonc to .json format, removing any comments in the process. filepath parameter specifies path to file and remove_file parameter specifies if .jsonc file will be removed (deleted from hard drive) after conversion. If set to True, this function will delete .jsonc file leaving only .json file. Otherwise, both files are not deleted. This function can raise _[FunctionParameterError](#exc-function-parameter-error)_ if _filepath_ parameter is not a path-like object or is empty or if _remove_file_ parameter is not a boolean.

- ##### JsoncParser.convert_to_jsonc(filepath: PathLike, remove_file: bool = False) -> None
This function converts file from .json to .jsonc format, enabling comment support. filepath parameter specifies path to file and _remove_file_ parameter specifies if .jsonc file will be removed (deleted from hard drive) after conversion. If set to True, this function will delete .jsonc file leaving only .json file. Otherwise, both files are not deleted.
This function can raise _[FunctionParameterError](#exc-function-parameter-error)_ if _filepath_ parameter is not a path-like object or is empty or if _remove_file_ parameter is not a boolean.

## Exceptions

There are a total of 3 custom exceptions that jsonc-parser can raise during its runtime. To access the in your script, simply import them from jsonc_parser.errors module:

from jsonc_parser.errors import FileError, IncorrectParameterError, ParserError

#### Exceptions:

- **FileError**
<div id='exc-file-error'></div>
This exception indicates that there is a problem with selected file.

- **FunctionParameterError**
<div id='exc-function-parameter-error'></div>
This exception indicates that some of function's parameters are invalid. They may have wrong type, have invalid values or be erroneous in some other way.

- **ParserError**
<div id='exc-parser-error'></div>
This exception indicates that file cannot be parsed. It can have wrong extension, invalid data, etc.
Empty file added jsonc_parser/__init__.py
Empty file.
35 changes: 35 additions & 0 deletions jsonc_parser/errors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
class FunctionParameterError(Exception):
"""
This excption indicates that one or more of function's parameters are incorrect
"""

def __init__(self, *args):
self.__msg = args[0]
super().__init__(self.__msg)

def __str__(self):
return self.__msg


class FileError(Exception):
"""
This exception indicates that file format cannot be parsed
"""

def __init__(self, *args: object) -> None:
super().__init__(*args)

def __str__(self) -> str:
return super().__str__()


class ParserError(Exception):
"""
This exception indicates that file cannot be parsed
"""

def __init__(self, *args: object) -> None:
super().__init__(*args)

def __str__(self) -> str:
return super().__str__()
5 changes: 5 additions & 0 deletions jsonc_parser/jsonc_parser/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
本文件夹中以下内容已移至上级目录:

- `__init__.py`
- `parser.py`
- `errors.py`
196 changes: 196 additions & 0 deletions jsonc_parser/parser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
import json
import re
from jsonc_parser.errors import FileError, FunctionParameterError, ParserError
import os
from typing import Union


class JsoncParser:

# regex = re.compile(r"//.*?\n|/\*.*?\*/", re.MULTILINE | re.DOTALL)
regex = re.compile(r"(\".*?\"|\'.*?\')|(/\*.*?\*/|//[^\r\n]*$)", re.MULTILINE | re.DOTALL)
newline_replace_regex = re.compile("\n{2,}", re.MULTILINE)

def parse_str(_string: str) -> dict:
"""
Parse JSON-as-string and deserialize its content into Python dictionary,
ignoring any comments.
Parameters: `_string:str` - path to file
This function will raise a `FunctionParameterError` exception if `_string` parameter
has an incorrect type or is empty.
This function will raise a `ParserError` exception if the string cannot be parsed.
This function will raise any additional exceptions if occurred.
"""

def __re_sub(match):
if match.group(2) is not None:
return ""
else:
return match.group(1)

if type(_string) != str:
raise FunctionParameterError(
"_string parameter must be str; got {} instead".format(type(_string).__name__)
)

try:
data = JsoncParser.regex.sub(__re_sub, _string)
return json.loads(JsoncParser.regex.sub(__re_sub, data))
except Exception as e:
raise ParserError("{} file cannot be parsed (message: {})".format(_string, str(e)))

@staticmethod
def parse_file(filepath: Union[str, os.PathLike], encoding: str = None) -> dict:
"""
Parse .jsonc file and deserialize its content into Python dictionary,
ignoring any comments.
Parameters:
`filepath: str | os.PathLike` - path to file
`encoding: str = None` - option of open()
This function will raise a `FunctionParameterError` exception if `filepath` parameter
has an incorrect type or is empty.
This function will raise a `FileError` exception if file format is unsupported.
This function will raise a `ParserError` exception if file cannot be parsed.
This function will raise any additional exceptions if occurred.
"""

def __re_sub(match):
if match.group(2) is not None:
return ""
else:
return match.group(1)

# verify that provided path is a valid path-like object
try:
filepath = os.fspath(filepath)
except TypeError:
raise FunctionParameterError(
"filepath parameter must be path-like; got {} instead".format(type(filepath).__name__)
)

if not filepath:
raise FunctionParameterError("path is empty.")

if not os.path.exists(filepath) or not os.path.isfile(filepath):
raise FileError("{} does not exist or is not a file".format(filepath))

if filepath.split(".")[-1] not in ["json", "jsonc"]:
raise FileError("file {} has an unsupported extension.".format(filepath))

json_file = open(filepath, "r", encoding=encoding)
data_raw = json_file.read()
json_file.close()

try:
data = JsoncParser.regex.sub(__re_sub, data_raw)
return json.loads(JsoncParser.regex.sub(__re_sub, data))
except Exception as e:
raise ParserError("{} file cannot be parsed (message: {})".format(filepath, str(e)))

@staticmethod
def convert_to_json(
filepath: Union[str, os.PathLike],
remove_file: bool = False,
encoding: str = None,
ensure_ascii: bool = True,
) -> None:
"""
Convert file from .jsonc to .json format, removing any comments from it
Parameters: `path: str | os.PathLike` is a path to file, `remove_file:bool` indicates if
source file will be deleted or not. If set to True, .jsonc file will be deleted from the
hard drive, otherwise file remains alongside with its .json output.
`encoding: str = None` - option of open()
`ensure_ascii: bool = True` - option of json.dump()
This function will raise a `FunctionParameterError` if one or more of function's parameters
has an incorrect type/invalid value.
This function will raise any additional exceptions if occurred.
"""

# verify that provided path is a valid path-like object
try:
filepath = os.fspath(filepath)
except TypeError:
raise FunctionParameterError(
"filepath parameter must be path-like; got {} instead".format(type(filepath).__name__)
)

if not filepath:
raise FunctionParameterError("path is empty.")

if type(remove_file) != bool:
raise FunctionParameterError(
"remove_file parameter must be bool; got {} instead.".format(
type(remove_file).__name__
)
)

data = JsoncParser.parse_file(filepath, encoding=encoding)

if remove_file:
os.remove(filepath)

new_filename = os.path.splitext(filepath)[0] + ".json"
if os.path.exists(new_filename) and os.path.isfile(new_filename):
raise FileError("{} file already exists".format(new_filename))

json_file = open(new_filename, "x", encoding=encoding)
json_file.write(json.dumps(data, indent=2, ensure_ascii=ensure_ascii))
json_file.close()

@staticmethod
def convert_to_jsonc(
filepath: Union[str, os.PathLike],
remove_file: bool = False,
encoding: str = None,
ensure_ascii: bool = True
):
"""
Convert file .jsonc format, enabling comments.
Parameters: `filepath: str | os.PathLike` is a path to file, `remove_file:bool` indicates
if source file will be deleted or not. If set to True, .json file will be deleted from the
hard drive, otherwise file remains alongside with its .jsonc output.
`encoding: str = None` - option of open()
`ensure_ascii: bool = True` - option of json.dump()
This function will raise a `FunctionParameterError` if one or more of function's parameters
has an incorrect type/invalid value.
This function will raise any additional exceptions if occurred.
"""
# verify that provided path is a valid path-like object
try:
filepath = os.fspath(filepath)
except TypeError:
raise FunctionParameterError(
"filepath parameter must be path-like; got {} instead".format(type(filepath).__name__)
)

if not filepath:
raise FunctionParameterError("path is empty.")

if type(remove_file) != bool:
raise FunctionParameterError(
"remove_file parameter must be bool; got {} instead.".format(
type(remove_file).__name__
)
)

data = JsoncParser.parse_file(filepath, encoding=encoding)

if remove_file:
os.remove(filepath)

new_filename = os.path.splitext(filepath)[0] + ".jsonc"
if os.path.exists(new_filename) and os.path.isfile(new_filename):
raise FileError("{} file already exists".format(new_filename))

json_file = open(new_filename, "x", encoding=encoding)
json_file.write(json.dumps(data, indent=2, ensure_ascii=ensure_ascii))
json_file.close()
Loading

0 comments on commit f757197

Please sign in to comment.