PDA

View Full Version : JSON and maxscript?


eek
04-03-2012, 06:56 AM
Has anyone used JSON with maxscript - i.e building there own importer/exporter or using a .net library?

Any suggestions of libraries etc?

Pjanssen
04-03-2012, 07:04 AM
I haven't used it in maxscript specifically, but JSON.NET is a good library:
http://json.codeplex.com/

eek
06-16-2012, 06:38 PM
Awesome! I will take a look, Im in python land currently which has a JSON module - I'll dig into the .net assembly or python max stuff.

LoneRobot
06-17-2012, 07:13 PM
Hey Charles, i was looking at this about a month ago. I found I was confused as to what extra it offered over code serialized as XML, unless I'm missing something important. Any insight as to the advantages?

eek
06-17-2012, 08:00 PM
Well i pretty much use max, maya, motionbuilder and am evaluating shotgun at work - three of which use python.

Shotgun uses the dictionary key/value pair assignments to store all its data - this makes it incredibly easy to store, update etc.. There methodology is CRUD with some additional methods: find, upload etc.. Assignment and reference is handled quite nicely:

{'type':'shot', 'id':2, 'project':{'type':'project', 'id':1}}

type = type of information
id = the informations id
data = data of the information, which can reference additional asses eg.
data = {'name':'charles', 'scene':{'type':'scene', 'id':2}}

Im just looking into humanly readable formats that are easy to update by hand or not, and are cross compatible.

Mambo4
02-22-2013, 07:54 PM
bumping this thread because the challenge of updating JSON via MAX script is my next priority.

eek, did you get the JSON.NET library working ? If so I'd love to see some examples of loading/parsing/saving json, as I'm new to dot net objects in max script.

[EDIT]
I decided to edit down my spam to keep this thread concise.

Mambo4
02-25-2013, 03:37 PM
[EDIT]
I decided to edit down my spam to keep this thread concise.

Mambo4
02-25-2013, 06:56 PM
[EDIT]
I decided to edit down my spam to keep this thread concise.

Mambo4
02-25-2013, 09:16 PM
[edit: deleting spam to minimize thread]

Mambo4
03-12-2013, 12:00 AM
actually making some headway with this. I will post updated code when I am no longer on a deadline.

Mambo4
03-18-2013, 06:30 PM
I've condensed what I've learned into on giant chunk of heavily commented code, below.

The basic idea is that we have MXS wrapping dotNet objects, these objects hold and manipulate Json data.

Whats really going on is MXS wraps dotNetObject wrapping LINQ objects Wrapping Json data -but thinking about it to hard complicates usage.

All we care about are 4 basic dotNet objects : JObject, JArray, Jproperty and JValue, and the properties and methods for their use. Briefly:

Newtonsoft.Json.Linq.JObject contains {< anything >} as its .value property, plus has methods and properties for manipulation.

Newtonsoft.Json.Linq.JArray contains[ any array] as its .value property, plus has methods and properties for manipulation.

...JProperty contains a single "<Key>":<value> pair, plus has methods and properties for manipulation."<key>" is the .name property, <value> is its .value.value property
(that's not a typo, it is .value.vlaue...see the code comments)

...JValue contains a single json value < string, number, boolean, JOBject or JArray), as its .value property plus has methods and properties for manipulation.

they are described breifly in the comments below, and with technical thouroughness in the JSON.NET documentation
http://james.newtonking.com/projects/json/help/


/*
First, some max script to read json files as strings.
*/

testFilePath="C:/path/to/my/jsonData.json"
/*
note the slash " / " instead of windows back slash.
this is just personal choice, to avoid using the double back slash
necessary to correctly escape the path delimiters
"C:\\path\\to\\my\\jsonData.json" is equally valid.
*/

fn getJsonFileAsString filePath=(
local jsonString=""
fs=openFile filePath
while not eof fs do(
jsonString += readchar fs
)
close fs
return jsonString
)

jsonString= getJsonFileAsString testFilePath

/*
Now we're ready for some action.
download the JSON.NET library from http://json.codeplex.com/
and copy
..\Json45r11\Bin\Net40\Newtonsoft.Json.dll
..\Json45r11\Bin\Net40Newtonsoft.Json.xml (not sure if this is necessary)
into
C:\Program Files\Autodesk\3ds Max 2013\
this will put the library where Max can see it.
*/

--use dotNet.loadAssembly to load the Newtonsoft.Json library:

json = dotNet.loadAssembly "Newtonsoft.Json.dll"

--#> dotNetObject:System.Reflection.RuntimeAssembly

/*

a look at http://json.codeplex.com/documentation reveals that most of the functions
are for loading JSON into a pre-defined C# object
whose keys and structure match *exactly* the JSON you are reading.
This is not a very flexible approach.

We want a dotNetObject that we can fully query and manipulate.
JSON.NET utilizes LINQ objects for this.

the class Newtonsoft.Json.Linq.JObject wraps json data in a LINQ object.

But using MaxScript wrapping DotNet Wrapping LINQ Wrapping JSON gets confusing fast!
It's challenging to excavate the acutal JSON data form all these wrappers.
Lets see if we can dig out useful json data...

*/

--first, instantiate a dotNetObject using the class Newtonsoft.Json.Linq.JObject. This creates an empty JObject.
myJObject=dotNetObject "Newtonsoft.Json.Linq.JObject"
--#> dotNetObject:Newtonsoft.Json.Linq.JObject

--next, use the Newtonsoft.Json.Linq.JObject.parse method to read our json string into the JObject we just made
myJObject=myJObject.parse jsonString
--#> dotNetObject:Newtonsoft.Json.Linq.JObject
--the method myJObject.parse has populated our JObject with the data in the string.

--get the first json object in myJObject
myFirstItem=myJObject.First
--#> dotNetObject:Newtonsoft.Json.Linq.JValue

--get the value of myFirstItem
myFirstItem.Value
--#>dotNetObject:Newtonsoft.Json.Linq.JValue

/*
hey, that's not a json value!
It's a dotNetObject of the Class JValue. It holds a JSON value,
along with some useful properties and methods for manipulating and searching json data.
*/

myFirstItem.Value.Value
--#> "something"

/*
sweet! first object's value is revelaed to max script. how about the key?
the creator of JSON.NET himself posted this C# function to generate a list of keys:
IList<string> keys = parent.Properties().Select(p => p.Name).ToList();
It won't work in Max script, of course. But it implies that keys are the .Name property of myFirstItem.value's .Parent property:
*/

myFirstItem.Value.Parent.Name
--#> "key"
-- same as :
myFirstItem.Name
--#> "key"
/*
cool, we can read the first ojects name and key.
what if we want to refer to the key and get the value?
*/
myJObject.item["key"].value

/*
THE JSON.NET OBJECT ZOO

so we have a small zoo of dotNetObjects to hold and manipulate json data.
The four basic types

JObject : JSON.NET class; a dotNetObject that represents a json object -anything between {} . Not actual JSON data,
but a container with its own properties and methods.

JArray : JSON.NET class; a dotNetObject that represents a json Array -anythign between []. Specifically, an array of
Jobjects, JArrays, JProperties, Jvalues.

JProperty: JSON.NET class; a dotNetObject that represents a Key:Value Pair.

JValue: JSON.NET class; a dotNetObject container for a value in JSON (string, integer, date, etc.)
*Not actual JSON data*, but a container with its own props and methods.
MOST CONFUSINGLY: JValue.value might retrun a simple value (string, number, boolean) OR any of the above objects.

lets see what these dotNetObjects can do for us...
*/

myJObject=myJObject.parse jsonString
--#> dotNetObject:Newtonsoft.Json.Linq.JObject
--returns a JObject : holds json data

myFirstItem=myJObject.First
--#> dotNetObject:Newtonsoft.Json.Linq.JProperty
--returns a JProperty : a key:value pair

myFirstItem.name
--#>"key"
--the key of the JProperty

myFirstItem.value --JValue object.
--#>dotNetObject:Newtonsoft.Json.Linq.JValue
--returns a JValue : the value portion of a key value pair.
--note this is not the actual value, but a dotNetObject for holding and manipulating the value

--reading a JSON value:
myFirstItem.value.value
--#>"myValue"
--JValue.value property; retruns the actual Json VALUE

--writing a JSON value:
myFirstItem.value.value = "newValue"
--#>"newValue"
--we set a new value for "key"


/*
Json arrays...
*/

myJArray=myJObject.Item["myJsonArray"]
--#>dotNetObject:Newtonsoft.Json.Linq.JArray
myJArray.item(0)
--#>dotNetObject:Newtonsoft.Json.Linq.JObject

/*
so myJArray.item[0] appears to contain a Json object
JArray.item() access is a dotnet function / method, using zero indexing,
so the usual mxs Array access with brackets and 1 indexing - "myArray[1]"- won't work.
the fact that we have to use () to access JArray elements creates some scoping issues in mxs.
lets take a look:
*/

--make a simple json array key:value pair, wrapped in a json object as a string
myJarray_string= "{myArray:[\"foo\",\"bar\", \"baz\"]}"
--#> "{myArray:["foo","bar", "baz"]}"

--instantiate a JObject, parse the string into the JObject
myJarray = dotNetObject "Newtonsoft.Json.Linq.JObject"
--#>dotNetObject:Newtonsoft.Json.Linq.JObject
myJarray = myJarray.Parse myJarray_string
--#>dotNetObject:Newtonsoft.Json.Linq.JObject


myJarray.item["myArray"]
--#>dotNetObject:Newtonsoft.Json.Linq.JArray
--the value of key "myArray" is a JArray object

myJarray.item["myArray"].item(0)
--#>dotNetObject:Newtonsoft.Json.Linq.JValue
-- myArray[1] is a JValue object, so we * should * be able to use .value to see the value...

myJarray.item["myArray"].item(0).value
--#>-- Error occurred in anonymous codeblock; filename: C:\Program Files\Autodesk\3ds Max 2013\scripts\; position: 633; line: 19
--#>-- Unknown property: "value" in 0
/*
uh oh, somethign hinky is going on. mxs is looking for "0.value" instead of "item(0).value".
it seems the () cause a scoping problem.
since <myJarray.item["myArray"].item(0) > really represents a single variable
we can fix this by wrapping the whole thing in another set of ():
*/
(myJarray.item["myArray"].item(0)).value
--#>"foo"

--although it maight be neater to just sue a variable
item1=myJarray.item["myArray"].item(0)
item1.value


/*
exposing more JSON.NET properties:
below is a function to expose the values of properties of JSON.NET JObjects and JObject subclasses
to use:
1.) instantiate a JObject
myJObject=dotNetObject
--#>"Newtonsoft.Json.Linq.JObject"
2.) populate it by parseing a JSON string
myJObject=o.parse < a Json String >
3.) pass *as a string* when calling the function
exposeProps "myJObject"
(NOT: exposeProps myJObject)
*/

fn exposeProps objString =(

obj=execute(objString)--evaluate string as actual object
for propName in (getPropNames obj) do (
p=propName as string
format "% % : %\n" objString p (execute(objString+"."+p))
)
)


/*
recursing unknown JSON
the above is all well and good if we know the JSON data we want to mess with.
but what if the JSON data is variable, or unkown?
below is a fucntion to recurse an unknown json object and pretty print its contents.
*/

--tab fucntions to format output
tab=""
fn tabIn=(tab += "\t")
fn tabOut=( try(tab = replace tab 1 1 "")catch())

--recursing unkown JSON
fn recurseJData jdata=(
tabIn()
--format "<%>" data
local data=jdata
local typeIndex=data.type.value__
local dType=JTokenTypes[typeIndex+1]

case data.hasValues of (
true:(
--data contains values
--action: deteemine if JProperty, object or array

case data.type.value__ of (

4:(--data is type JProperty ( a key:value pair )
--action: print key, recurse value
--you could begin storing the keys in a Maxscript array or whatever

local key=data.name
local val=data.value
format "\n%%:"tab key
recurseJData val

)

1: (
--Data is a Json Object
--tabIn()
format "\n%{" tab
local oc=0
local objData=data.first
for oc=1 to data.count do(
if objData == undefined do(format " ERROR: obj: %" objdata; continue)
recurseJData(objData)
--format "%NEXT:%" tab childData.next
objData=objData.next

)
oc=0
format "\n%}" tab
--tabOut()
)

2: (--Data is a Json Array
--tabIn()
format "\n%[" tab
local jArray=data
for i=1 to jArray.count do(

element=jArray.item(i-1)
--if element == undefined do(format " !!!%" elementData; continue)
recurseJData(element)

)
--tabOut()
format "\n%]" tab


)
)

)

false:(
--data IS a value
--action: print value
case data.type.value__ of (
6:(--integer
format " %"data.value
)
7:(--float
format " %"data.value
)
8:(--string
format "\"%\""data.value
)
9:(--boolean
format " %"data.value
)
default: (
format "% WARNING: unexpected JTokenType (%-%) in {%}" tab typeIndex dType currentItem
)

)

)
)
tabOut()
)


/*

and finaly, once we are done with or JSON object and wish to write it to a file,
we can use the JObject.toString() method to gnerate a string fo json
and then use mxs format <string> to: <stream> to write the json file
*/
fn writeJsonObjectToFile jsonObject filePath =(
format " SAVE: %\n" filePath
local jsonString=jsonObject.toString()
print jsonString--just to verify in listener
--open filepath as stream, use a writable mode
fs= openFile filePath mode:"wt" --open specified json file
format "%" jsonString to:fs--write string to the file
close fs
format " SAVED: %\n" filePath --save
)


Some of this is doubtless sloppy code, but I did get it working for my purposes.

What I's really love to make is some kind of dynamic, recursive native mxs object for json data, so lines like :
myJObject.item["key"].value.value="foo" could be replaced by a simpler
myJObject.key="foo"

structs , which I'm admittedly not too farmiliar with, seem too static and non recursive form the examples...

[update 3.20.2013] found some scoping issues with JArray syntax, updated code and comments.

CGTalk Moderation
03-18-2013, 06:30 PM
This thread has been automatically closed as it remained inactive for 12 months. If you wish to continue the discussion, please create a new thread in the appropriate forum.