Notifications
Clear all

[Closed] JSON parsing with vanilla dotnet

I’m trying to parse JSON string as array ot XM/
I was trying to use DataContractJsonSerializer.
But, it is just not working.
Any help?

11 Replies

I wrote a JSON.NET wrapper in mxs you can download here

Maybe you can make use of it, ask me any questions…

Of course your approach could be failing because the JSON is invalid…did you run the JSON through jsonlint.com ?

I think Chansoo wants to be able to do this without 3rd party json libraries. Although max 2017 might make this completely redundant, this might be of use for people on older versions.

Unfortunately you have to register a custom data class to make it work easily. Are you reading the json string in from a file? you might need to know what the structure of it is in order to build the serializer class object. If you have the Json you can use http://json2csharp.com/ to generate your class definition in order for the serializer to work.


-- define a custom c# class to hold your data and tell the serializer what's what
fn mxsJsonClass =
(
	dotnet.loadassembly "System.Runtime.Serialization"
	
	if classof (dotnet.GetType "mxsJsonData")!=dotNetObject then
	(	
	local source ="using System;
					using System.Runtime.Serialization;

					[DataContract]
					public class mxsJsonData
					{
						[DataMember]
						public string nodename;
						[DataMember]
						public int serial;
						[DataMember]
						public string assetType;    
					}
					"

	csharpProvider = dotnetobject "Microsoft.CSharp.CSharpCodeProvider"
	compilerParams = dotnetobject "System.CodeDom.Compiler.CompilerParameters"
	compilerParams.ReferencedAssemblies.Add("System.dll");
	compilerParams.ReferencedAssemblies.Add("System.Runtime.Serialization.dll");
	compilerResults = csharpProvider.CompileAssemblyFromSource compilerParams #(source)	
	assembly = compilerResults.CompiledAssembly
	assembly.CreateInstance "mxsJsonData"	
	)
	else dotnetobject "mxsJsonData"		
)
	
-- thanks to  https://blog.udemy.com/c-sharp-json/ 
-- write custom data to JSON format via vanilla C#

jsonOutFile = @"C:	emp\json\OutputFileTest.json"

-- build the custom class and put some data into it
mxsJson = mxsJsonClass()
mxsJson.assetType = "Character"
mxsJson.nodename = "Dave_Mesh"
mxsJson.serial = 412590
 	
-- not sure if we really need this
jsonSettings =	dotnetobject "System.Runtime.Serialization.Json.DataContractJsonSerializerSettings"	
jsonsettings.UseSimpleDictionaryFormat  = true

--build the serializer object
js = dotnetobject "System.Runtime.Serialization.Json.DataContractJsonSerializer" (mxsJson.getType()) jsonSettings	
	
stream = dotnetobject "System.IO.MemoryStream"	
js.WriteObject stream mxsJson
fs = dotnetobject "system.IO.filestream" jsonOutFile ((dotnetclass "system.io.filemode").create) ((dotnetclass "system.io.fileaccess").write)	
stream.writeTo fs
fs.close()
stream.close()

-- read external data from Json into C#
-- the file we just wrote will do

info = dotnetobject "system.IO.FileInfo" jsonOutFile
readFs = dotnetobject "system.IO.filestream" jsonOutFile ((dotnetclass "system.io.filemode").open) ((dotnetclass "system.io.fileaccess").read)
br = dotnetobject "system.IO.BinaryReader" readFs
jsonBytes = br.ReadBytes info.Length
readFs.close()	

rStream = dotnetobject "System.IO.MemoryStream"	jsonBytes
inJson = js.ReadObject rStream
format "Json In:
	%
	%
	%
" inJson.nodename inJson.assetType inJson.serial
 
-- write the same data we just loaded in out to an xml file
xmlOutFile = @"C:	emp\json\OutputFileTest.xml"
xmlSet = dotnetobject "System.XML.XmlWriterSettings" 
xmlSet.indent = true
xmlWriter = (dotnetclass "System.XML.XmlWriter").create xmlOutFile xmlSet
js.WriteObject xmlWriter inJson
xmlWriter.close()

Thanks, guys.

As of now, I use JSON.NET.
But, I still want to do without 3rd party.

My input source is text string.
My main problem is how to feed string to DataContractJsonSerializer.

I checked some tutorials on the web.
Most of them said I need to convert string to memory stream, and I failed around there.

The json daata i’m getting does not have nested data.
All I need to do is…

  1. Get the list of keys.
  2. get Value of each key.

So the problem is how to turn the json string in to a memory stream

Here is Stack Overflow post about that

also it looks like the Class of the object you deserialize into needs a [DataContract] decorator…

Ah
Its sounds like you may not know the keys expected ahead of time.
So the real problem is you don’t have a pre constructed C# object ahead of time to serialize into.

Do you know what keys to expect?
Because pretty much every example of how to use JSON in C# assumes this is so.

If the json really is not nested, you can use MXS to make a simple string parser

fn JsonStringToKeyValuePairArray jString=
(
	
	--an array of json charcaters to strip out (regex may be faster)
	jsonChars=#("{","}","
","\"")

	--strip them out
	for each in jsonChars do(jString=substitutestring jString each "" )

	--filterstring for each "," to make an array of pairs
	KeyValuePairArray=filterstring jString ","

	--loop through each pair and filterstring for ":" to build a 2 element array #(key,value)
	--add the resulting Key Value Pair to an array (there even may be a way to uiltize MXS DataPair values here.)
	KVPArray=#()
	for each in KeyValuePairArray do
	(
		pair=filterstring each ":"
		key=pair[1]
		val=pair[2]
		append KVPArray #(key,val)
	)
	
	--report on the result
	for each in KVPArray do
	(
		key= each[1] 
		val= each[2]
		format "	key: %	Value: %
"
	)

	KVPArray
)

--a sample Json string to test
jString= "{
\"foo\":\"bar\",
\"qux\":\"baz\",
\"zig\":\"zag\"
}"


JsonStringToKeyValuePairArray jString


THis is what I got so far

assetStr = "{\"Asset Location\":\"la\",\"Asset Name\":\"main\",\"Author\":\"changsooeun\"}"
dotNet.LoadAssembly "System.Runtime.Serialization.Json.dll"
dnJson = dotNetObject "System.Runtime.Serialization.Json.DataContractJsonSerializer" (dotnetClass "system.string")
utf8enc = (dotNetClass "System.Text.Encoding").UTF8
assetMStrm = (dotNetObject "System.IO.MemoryStream" (utf8enc.GetBytes(assetStr)))
dnJson.readObject assetMStrm

And,. I got this error.

– Error occurred in anonymous codeblock; filename: S:\outfit\artists\Changsoo\MXS\WIP__URL.ms; position: 8381; line: 198
– Frame:
– dbAsset: StructDef:dbAsset
– assetStr: “{“Asset Location”:“la”,“Asset Name”:“main”,“Author”:“changsooeun”}”
– utf8enc: dotNetObject:System.Text.UTF8Encoding
– assetMStrm: dotNetObject:System.IO.MemoryStream
– dnJson: dotNetObject:System.Runtime.Serialization.Json.DataContractJsonSerializer
– Runtime error: dotNet runtime exception: End element ‘root’ from namespace ‘’ expected. Found element ‘a:item’ from namespace ‘item’.

this line

dnJson = dotNetObject "System.Runtime.Serialization.Json.DataContractJsonSerializer" (dotnetClass "system.string")


is telling[B] [I]DataContractJsonSerializer[/I][/B] that the Type of object you are feeding the data to is [B][I]System.String[/I][/B]
[B][I]System.String[/I][/B] does not have property fields that match the data 
  • there is no System.String.Author property, for example.

    you need to:
    1.) construct a C# class that can hold the data and prefix it with a decorator [DataContract]
    2.) Pass that class as a type to your DataContractJsonSerializer

    like shown here
    http://stackoverflow.com/questions/14959698/how-to-use-datacontractjsonserializer-for-json

    This is probably better done in pure C# rather than as maxscript
    I would code up dll that has the class to hold the data and serializes it.

    but if all you need to do is get the values
    and those keys never change
    you can use an Max Script struct to hold the data

	
	struct assetData (
		Asset_Location,
		Asset_Name,
		Author
	)
	
you can combine this with my previous code to produce a Max Script object that holds the Json values
	
	fn JsonStringToAssetData jString=
(
	--an array of json charcaters to strip out (regex may be faster)
	jsonChars=#("{","}","
","\"")

	--strip them out
	for each in jsonChars do (jString=substitutestring jString each "" )

	--filterstring for each "," to make an array of pairs
	KeyValuePairArray=filterstring jString ","

	--loop through each pair and filterstring for ":" to build a 2 element array #(key,value)
	--add the resulting Key Value Pair to an array (there even may be a way to uiltize MXS DataPair values here.)
	KVPArray=#()
	for each in KeyValuePairArray do
	(
		pair=filterstring each ":"
		key=pair[1]
		val=pair[2]
		append KVPArray #(key,val)
	)
	
	--struct to hold the data
	struct assetDataStruct (
		Asset_Location,
		Asset_Name,
		Author
	)
	
	--make an instance of the struct
	myAssetData=assetDataStruct()
	
	--loop through the array of pairs and add values to the myAssetData struct instance
	for each in KVPArray do
	(
		key= each[1] 
		val= each[2]
		--format "	key: %	Value: %
" key Val
		if key=="Asset Location" then myAssetData.Asset_Location=val
		if key=="Asset Name" then myAssetData.Asset_Name=val
		if key=="Author" then myAssetData.Author=val
	)

	myAssetData
)

--a sample Json string to test
assetStr = "{\"Asset Location\":\"la\",\"Asset Name\":\"main\",\"Author\":\"changsooeun\"}"

myAssetData = JsonStringToAssetData assetStr

--output result

format "myAssetData.Asset_Location: %
myAssetData.Asset_Name: %
myAssetData.Author: %
" myAssetData.Asset_Location myAssetData.Asset_Name myAssetData.Author

this function will return a max script object with the values you need (if I understand you correctly)

This is a simple method that assumes your data structure remains the same and the values do not contain unusual strings that may get mistaken for Json delimiters

Thanks for pointer. At least now I know what;s going on.

Unfortunately string will have all kinds of unusual strings…

How about this?
https://mutelight.org/using-the-little-known-built-in-net-json-parser

I’ll perfectly be happy if ai can converto XML.

Page 1 / 2