MaterialXJSON 1.39.0
Loading...
Searching...
No Matches
core.py
Go to the documentation of this file.
1# core.py
2
3'''
4@file
5This module contains the core definitions and utilities for reading and wring MaterialX
6in JSON format.
7'''
8
9# MaterialX support
10import MaterialX as mx
11
12# JSON support
13import json
14
15# Utilities
16import os
17
18# Mime type
19JSON_MIMETYPE_KEY = 'mimetype'
20JSON_MIMETYPE: str = 'application/mtlx+json'
21# We use a colon to separate the category and name of an element in the JSON hierarchy
22JSON_CATEGORY_NAME_SEPARATOR: str = ':'
23# The root of the JSON hierarchy
24MATERIALX_DOCUMENT_ROOT: str = 'materialx'
25
26# Special names for child element grouping. These do not correspond to any MaterialX syntax
27INPUTS_STRING = 'inputs'
28OUTPUTS_STRING = 'outputs'
29CHILDREN_STRING = 'children'
30
32 '''
33 Class for holding options for writing MaterialX to JSON.
34
35 Options:
36 - elementPredicate: MaterialX function predicate for filtering elements to write
37 - indent: The number of spaces to indent the JSON hierarchy
38 - separators: JSON separators. Default is: (',', ': ')
39 - addInputOutputCategories: Add input and output categories to JSON elements. Default is False
40 '''
41 def __init__(self):
42 '''
43 @brief Constructor
44 '''
45 self.elementPredicate: mx.ElementPredicate = None
46 self.indent = None
47 self.separators = (',', ': ')
48 self.addInputOutputCategories = True
49
51 '''
52 Class for holding options for reading MaterialX from JSON
53
54 Options:
55 - upgradeVersion: Upgrade the MaterialX document to the latest version
56 '''
57 def __init__(self):
58 '''
59 @brief Constructor
60 '''
61 self.upgradeVersion = True
62
64 '''
65 Class for handling read and write of MaterialX from and to JSON
66 '''
67 def elementToJSON(self, elem: mx.Element, jsonParent: dict, writeOptions: JsonWriteOptions = None) -> dict:
68 '''
69 @brief Convert an MaterialX XML element to JSON.
70 Will recursively traverse the parent/child Element hierarchy.
71 @param elem The MaterialX element to convert
72 @param jsonParent The JSON element append to
73 @param writeOptions The write options to use. Default is None
74 '''
75 if (writeOptions and writeOptions.elementPredicate and not writeOptions.elementPredicate(elem)):
76 return
77
78 if (elem.getSourceUri() != ""):
79 return
80
81 # Create a new JSON element for the MaterialX element
82 jsonElem = {}
83 jsonElem['name'] = elem.getName()
84 category = elem.getCategory()
85 # It is redundant but not incorrect to add in the category
86 # For now always add in the category
87 if (writeOptions and writeOptions.addInputOutputCategories) or (category not in ['input', 'output']):
88 jsonElem['category'] = category
89
90 # Add attributes
91 for attrName in elem.getAttributeNames():
92 jsonElem[attrName] = elem.getAttribute(attrName)
93
94 # Add children. Split based on category: input, output or other
95 inputs = []
96 outputs = []
97 non_input_outputs = []
98 for child in elem.getChildren():
99 category = child.getCategory()
100 if category == 'input':
101 self.elementToJSON(child, inputs)
102 elif category == 'output':
103 self.elementToJSON(child, outputs)
104 else:
105 self.elementToJSON(child, non_input_outputs)
106
107 # Add inputs, outputs and other children
108 if len(inputs) > 0:
109 jsonElem[INPUTS_STRING] = inputs
110 if len(outputs) > 0:
111 jsonElem[OUTPUTS_STRING] = outputs
112 if len(non_input_outputs) > 0:
113 jsonElem[CHILDREN_STRING] = non_input_outputs
114
115 # Add the JSON element to the parent
116 jsonParent.append(jsonElem)
117
118 return jsonParent
119
120 def documentToJSON(self, doc: mx.Document, writeOptions: JsonWriteOptions = None) -> dict:
121 '''
122 Convert an MaterialX XML document to JSON
123 @param doc The MaterialX document to convert
124 @param writeOptions The write options to use. Default is None
125 @return The JSON document
126 '''
127 root = {}
128 # Set the mimetype
129 root[JSON_MIMETYPE_KEY] = JSON_MIMETYPE
130
131 # Create the document
132 documentRoot = {}
133
134 # Add document level attributes
135 for attrName in doc.getAttributeNames():
136 documentRoot[attrName] = doc.getAttribute(attrName)
137
138 # Add children
139 children = []
140 for elem in doc.getChildren():
141 self.elementToJSON(elem, children, writeOptions)
142 documentRoot['children'] = children
143
144 # Set 'materialx' root element
145 root[MATERIALX_DOCUMENT_ROOT] = documentRoot
146
147 return root
148
149 def documentToJSONString(self, doc: mx.Document, writeOptions: JsonWriteOptions = None) -> str:
150 '''
151 Convert an MaterialX XML document to JSON string
152 @param doc The MaterialX document to convert
153 @param writeOptions The write options to use. Default is None
154 @return The JSON string
155 '''
156 result = self.documentToJSON(doc, writeOptions)
157 json_string = ''
158 if result:
159 indentation = 2
160 sep = (',', ': ')
161 if writeOptions:
162 indentation = writeOptions.indent
163 sep = writeOptions.separators
164 json_string = json.dumps(result, indent=indentation, separators=sep)
165
166 return json_string
167
168 def elementFromJSON(self, node: dict, elem: mx.Element, readOptions: JsonReadOptions = None) -> None:
169 '''
170 @brief Convert an JSON element to MaterialX
171 @param node The JSON element to read
172 @param elem The MaterialX element to write to
173 @param readOptions The read options to use. Default is None
174 '''
175 for key in node:
176 value = node[key]
177
178 # Set attributes
179 if isinstance(value, str):
180 if key not in ['name', 'category']:
181 elem.setAttribute(key, str(value))
182
183 # Traverse children
184 elif key == INPUTS_STRING:
185 for child in value:
186 category = 'input'
187 name = child['name']
188 childElem = elem.addChildOfCategory(category, name)
189 self.elementFromJSON(child, childElem)
190
191 elif key == OUTPUTS_STRING:
192 for child in value:
193 category = 'output'
194 name = child['name']
195 childElem = elem.addChildOfCategory(category, name)
196 self.elementFromJSON(child, childElem)
197
198 elif key == CHILDREN_STRING:
199 for child in value:
200 category = child['category']
201 name = child['name']
202 childElem = elem.addChildOfCategory(category, name)
203 self.elementFromJSON(child, childElem)
204
205 def documentFromJSON(self, jsonDoc: dict, doc: mx.Document, readOptions: JsonReadOptions = None) -> bool:
206 '''
207 @brief Convert a JSON document to MaterialX
208 @param jsonDoc The JSON document to read
209 @param doc The MaterialX document to write to
210 @param readOptions The read options to use. Default is None
211 '''
212 readDoc = False
213 # Check mimetype and existence of MaterialX root element
214 if JSON_MIMETYPE_KEY in jsonDoc and jsonDoc[JSON_MIMETYPE_KEY] == JSON_MIMETYPE:
215 if MATERIALX_DOCUMENT_ROOT in jsonDoc:
216 self.elementFromJSON(jsonDoc['materialx'], doc, readOptions)
217 readDoc = True
218 else:
219 print('JSON document is missing a MaterialX root element')
220 else:
221 print('JSON document is not a MaterialX document')
222
223 if readDoc:
224 # Upgrade to latest version if requested
225 if readOptions and readOptions.upgradeVersion:
226 doc.upgradeVersion()
227
228 return readDoc
229
230 def documentFromJSONString(self, jsonString: str, doc: mx.Document, readOptions: JsonReadOptions = None) -> bool:
231 '''
232 @brief Convert a JSON document to MaterialX
233 @param jsonString The JSON string to read
234 @param doc The MaterialX document to write to
235 @param readOptions The read options to use. Default is None
236 @return True if successful, false otherwise
237 '''
238 jsonDoc = json.loads(jsonString)
239 readDoc = False
240 if jsonDoc:
241 readDoc = self.documentFromJSON(jsonDoc, doc, readOptions)
242 return readDoc
243
244class Util:
245 '''
246 Utility class for MaterialX JSON
247 '''
248 @staticmethod
249 def readJson(fileName: str) -> dict:
250 '''
251 @brief Read a JSON file
252 @param fileName The file name to read
253 @return The JSON document
254 '''
255 jsonFile = open(fileName, 'r')
256 if not jsonFile:
257 return False
258
259 jsonObject = json.load(jsonFile)
260 if not jsonObject:
261 return False
262
263 return jsonObject
264
265 @staticmethod
266 def jsonStringToJson(jsonString: str) -> dict:
267 '''
268 @brief Convert a JSON string to a JSON document
269 @param jsonString The JSON string to convert
270 @return The JSON document
271 '''
272 return json.loads(jsonString)
273
274 @staticmethod
275 def jsonToJSONString(jsonObject: dict, indentation = 2) -> str:
276 '''
277 @brief Convert a JSON document to a JSON string
278 @param jsonObject The JSON document to convert
279 @return The JSON string
280 '''
281 return json.dumps(jsonObject, indent=indentation)
282
283 @staticmethod
284 def documentToXMLString(doc: mx.Document) -> str:
285 '''
286 @brief Convert a MaterialX document to XML string
287 @param doc The MaterialX document to convert
288 @return The XML string
289 '''
290 return mx.writeToXmlString(doc)
291
292 @staticmethod
293 def xmlStringToDocument(doc: mx.Document, xmlString: str) -> None:
294 '''
295 @brief Convert an XML string to a MaterialX document
296 @param doc The MaterialX document to write to
297 @param xmlString The XML string to convert
298 '''
299 mx.readFromXmlString(doc, xmlString)
300
301 @staticmethod
302 def writeJson(jsonObject: dict, fileName: str, indentation = 2) -> None:
303 '''
304 @brief Write a JSON document to file
305 @param jsonObject The JSON document to write
306 @param fileName The file name to write to
307 '''
308 with open(fileName, 'w') as outfile:
309 json.dump(jsonObject, outfile, indent=indentation)
310
311 @staticmethod
312 def getFiles(rootPath: str, extension: str) -> list:
313 '''
314 @brief Get all files with the given extension from the given root path
315 @param rootPath The root path to search from
316 @param extension The extension to search for
317 @return A list of file paths
318 '''
319 filelist = []
320 exts = (extension, extension.upper() )
321 for subdir, dirs, files in os.walk(rootPath):
322 for file in files:
323 if file.lower().endswith(exts):
324 filelist.append(os.path.join(subdir, file))
325 return filelist
326
327 @staticmethod
328 def loadLibraries(searchPath: mx.FileSearchPath, libraryFolders: list) -> tuple:
329 '''
330 @brief Load all libraries from the given search path and library folders
331 @param searchPath The search path to use
332 @param libraryFolders The library folders to use
333 @return A tuple containing the library document and a status string
334 '''
335 status = ''
336 lib = mx.createDocument()
337 try:
338 libFiles = mx.loadLibraries(libraryFolders, searchPath, lib)
339 status = '- Loaded %d library definitions from %d files' % (len(lib.getNodeDefs()), len(libFiles))
340 except mx.Exception as err:
341 status = '- Failed to load library definitions: "%s"' % err
342
343 return lib, status
344
345 @staticmethod
346 def jsonFileToXml(fileName: str, readOptions: JsonReadOptions = None) -> mx.Document:
347 '''
348 @brief Convert a JSON file to an XML file
349 @param fileName The file name to read from
350 @param readOptions The read options to use. Default is None
351 @return The MaterialX document if successful, None otherwise
352 '''
353 mtlxjson = MaterialXJson()
354
355 jsonFile = open(fileName, 'r')
356 if not jsonFile:
357 return None
358 jsonObject = json.load(jsonFile)
359 if not jsonObject:
360 return None
361
362 newDoc = mx.createDocument()
363 readDoc = mtlxjson.documentFromJSON(jsonObject, newDoc, readOptions)
364 if readDoc:
365 return newDoc
366
367 return None
368
369 @staticmethod
370 def jsonFileToXmlFile(fileName: str, outputFilename: str, readOptions: JsonReadOptions = None) -> bool:
371 '''
372 @brief Convert a JSON file to an XML file
373 @param fileName The file name to read from
374 @param outputFilename The file name to write to
375 @param readOptions The read options to use. Default is None
376 @return True if successful, false otherwise
377 '''
378 jsonFile = open(fileName, 'r')
379 if not jsonFile:
380 return None
381 jsonObject = json.load(jsonFile)
382 if not jsonObject:
383 return None
384
385 #newDoc = mx.createDocument()
386 mtlxjson = MaterialXJson()
387 newDoc = mx.createDocument()
388 created = mtlxjson.documentFromJSON(jsonObject, newDoc, readOptions)
389
390 if newDoc.getChildren():
391 mx.writeToXmlFile(newDoc, outputFilename)
392 return True
393
394 return False
395
396 @staticmethod
397 def xmlFileToJsonFile(xmlFileName: str, jsonFileName: str, writeOptions: JsonWriteOptions = None) -> None:
398 '''
399 Convert an MaterialX XML file to a JSON file
400 @param xmlFileName The XML file to read from
401 @param jsonFileName The JSON file to write to
402 @param writeOptions The write options to use. Default is None
403 '''
404 mtlxjson = MaterialXJson()
405
406 doc = mx.createDocument()
407 mx.readFromXmlFile(doc, xmlFileName)
408 if doc:
409 # Convert entire document to JSON
410 doc_result = mtlxjson.documentToJSON(doc, writeOptions)
411
412 # Write JSON to file
413 with open(jsonFileName, 'w') as outfile:
414 indentation = 2
415 sep = (',', ': ')
416 if writeOptions:
417 indentation = writeOptions.indent
418 sep = writeOptions.separators
419 json.dump(doc_result, outfile, indent=indentation, separators=sep)
420
Class for holding options for reading MaterialX from JSON.
Definition core.py:50
__init__(self)
Constructor.
Definition core.py:57
Class for holding options for writing MaterialX to JSON.
Definition core.py:31
mx.ElementPredicate elementPredicate
Definition core.py:45
__init__(self)
Constructor.
Definition core.py:41
Class for handling read and write of MaterialX from and to JSON.
Definition core.py:63
None elementFromJSON(self, dict node, mx.Element elem, JsonReadOptions readOptions=None)
Convert an JSON element to MaterialX.
Definition core.py:168
dict elementToJSON(self, mx.Element elem, dict jsonParent, JsonWriteOptions writeOptions=None)
Convert an MaterialX XML element to JSON.
Definition core.py:67
bool documentFromJSON(self, dict jsonDoc, mx.Document doc, JsonReadOptions readOptions=None)
Convert a JSON document to MaterialX.
Definition core.py:205
dict documentToJSON(self, mx.Document doc, JsonWriteOptions writeOptions=None)
Convert an MaterialX XML document to JSON.
Definition core.py:120
str documentToJSONString(self, mx.Document doc, JsonWriteOptions writeOptions=None)
Convert an MaterialX XML document to JSON string.
Definition core.py:149
bool documentFromJSONString(self, str jsonString, mx.Document doc, JsonReadOptions readOptions=None)
Convert a JSON document to MaterialX.
Definition core.py:230
Utility class for MaterialX JSON.
Definition core.py:244
None xmlFileToJsonFile(str xmlFileName, str jsonFileName, JsonWriteOptions writeOptions=None)
Convert an MaterialX XML file to a JSON file.
Definition core.py:397
mx.Document jsonFileToXml(str fileName, JsonReadOptions readOptions=None)
Convert a JSON file to an XML file.
Definition core.py:346
dict jsonStringToJson(str jsonString)
Convert a JSON string to a JSON document.
Definition core.py:266
str documentToXMLString(mx.Document doc)
Convert a MaterialX document to XML string.
Definition core.py:284
list getFiles(str rootPath, str extension)
Get all files with the given extension from the given root path.
Definition core.py:312
bool jsonFileToXmlFile(str fileName, str outputFilename, JsonReadOptions readOptions=None)
Convert a JSON file to an XML file.
Definition core.py:370
None writeJson(dict jsonObject, str fileName, indentation=2)
Write a JSON document to file.
Definition core.py:302
dict readJson(str fileName)
Read a JSON file.
Definition core.py:249
tuple loadLibraries(mx.FileSearchPath searchPath, list libraryFolders)
Load all libraries from the given search path and library folders.
Definition core.py:328
str jsonToJSONString(dict jsonObject, indentation=2)
Convert a JSON document to a JSON string.
Definition core.py:275
None xmlStringToDocument(mx.Document doc, str xmlString)
Convert an XML string to a MaterialX document.
Definition core.py:293