Unable to stringify JSON of 3D model

I have a 3D model in memory that I can call toJSON() on, and store that JSON version of the object in a var. When I try to call JSON.stringify(model), I get an error:

Uncaught TypeError: Cannot read property ‘[uuid appears here]’ of undefined

The uuid referenced in the error is not anywhere in the JSON of the model as I traverse it in the console (using Chrome).

Any ideas?

Do you mind posting a little live demo with your code?

I’m afraid I can’t do that. The code is proprietary and I can’t expose it. I was hoping someone might have encountered a problem like this and know what could be causing it. I do think I’ve nailed down that amongst the child objects there is a texture or image that is not applied but still retains some reference. Looking into it.

I am seeming to have the same problem. Upon running JSON.stringify() on the objects in my scene and resolving initial circular reference issues, I ran into the issue described here and spent most of the morning trying to find the needle in the haystack.

From what I can tell so far, the relevant Chrome console error details are:

Uncaught TypeError: Cannot read property 'F8643491-A1A0-4E7C-9125-340462AA78B6' of undefined
at b (three.min.js:486)
at Ac.toJSON (three.min.js:487)
at JSON.stringify (<anonymous>)  

Using Console scope from inside my circularRefsHandler() function I identified the culprit UUID as that of a SpriteMaterial:

Local
key:"text"
refs:"node"
returns:null
this:NodeLabel
	color:{r: 128, g: 128, b: 128}
	colorAsHex:ƒ ()
	fontsize:64
	isNodeLabel:true
	node:Node {isNode: true, id: "n00", position: {…}, radius: 0.5, shape: "sphere", …}
	opacity:0.2
	text:"n00"
	textSprite:Ac
		castShadow:false
		children:[]
		frustumCulled:true
		isGraphElement:true
		layers:Rd {mask: 1}
		material:cb
			alphaTest:0
			blendDst:205
			blendDstAlpha:null
			blendEquation:100
			blendEquationAlpha:null
			blendSrc:204
			blendSrcAlpha:null
			blending:1
			clipIntersection:false
			clipShadows:false
			clippingPlanes:null
			color:H {r: 1, g: 1, b: 1}
			colorWrite:true
			depthFunc:3
			depthTest:true
			depthWrite:true
			dithering:false
			fog:false
			lights:false
			map:X {uuid: "B0A11FCB-BF6A-42AF-9A57-0DE2E25B3A00", name: "", image: canvas, mipmaps: Array(0), mapping: 300, …}
			name:""
			needsUpdate:true
			opacity:1
			overdraw:0
			polygonOffset:false
			polygonOffsetFactor:0
			polygonOffsetUnits:0
			precision:null
			premultipliedAlpha:false
			rotation:0
			shading:2
			side:0
			transparent:false
			type:"SpriteMaterial"
			uuid:"F8643491-A1A0-4E7C-9125-340462AA78B6" (*****This is it!!!!!!*****)
			vertexColors:0
			visible:true
			id:21
			wrapAround:(...)
			wrapRGB:(...)
			__proto__:Z
		matrix:J {elements: Array(16)}
		matrixAutoUpdate:true
		matrixWorld:J {elements: Array(16)}
		matrixWorldNeedsUpdate:false
		name:""
		onAfterRender:ƒ ()
		onBeforeRender:ƒ ()
		parent:md {uuid: "7AF58FDA-1D70-445E-A87F-1D07128FA3F1", name: "", type: "Scene", parent: null, children: Array(162), …}
		position:p {x: 0, y: 15.25731, z: 8.50651}
		quaternion:qa {_x: 0, _y: 0, _z: 0, _w: 1, onChangeCallback: ƒ}
		receiveShadow:false
		referent:NodeLabel {isNodeLabel: true, text: "n00", node: Node, fontsize: 64, color: {…}, …}
		renderOrder:0
		rotation:bb {_x: 0, _y: 0, _z: 0, _order: "XYZ", onChangeCallback: ƒ}
		scale:p {x: 4, y: 2, z: 1}
		type:"Sprite"
		up:p {x: 0, y: 1, z: 0}
		userData:{}
		uuid:"461C8DFC-CAB0-43F2-817D-C644909CA577"
		visible:true
		z:28.50651
		eulerOrder:(...)
		id:18
		modelViewMatrix:J {elements: Array(16)}
		normalMatrix:Ka {elements: Array(9)}
		useQuaternion:(...)
		__proto__:B
	transformOnDblClick:ƒ ()
	transformOnMouseOut:ƒ ()
	transformOnMouseOver:ƒ ()
	transformOnMouseOverNode:ƒ ()
	transformOnNodeMouseOut:ƒ ()
	transformOnWheel:ƒ ()
	unTransformOnDblClickOutside:ƒ ()
	__proto__:Object
value:"n00"

Interestingly enough, MeshBasicMaterial appears to have passed through just fine, as did the sprite itself.

Here’s the code (I think) is relevant:

Sprite definition:

var _Label = {

/**
 * sprite();
 * 
 * @author Mark Scott Lavin /
 * modified from http://stemkoski.github.io/Three.js/Labeled-Geometry.html
 *
 * parameters = {
 *  fontface: <string>,
 *  fontsize: <int>,
 *  opacity: <float> between 0 & 1,
 *  textLineThickness <int>,
 *  spriteAlignment <THREE.SpriteAlignment>
 *  color: <object> { r: <integer>, g: <integer>, b: <integer> }
 *  opacity: <float> between 0 & 1,
 * }
 */	

sprite: function( message, parameters ){
	if ( parameters === undefined ) parameters = {};
	
	this.fontface = parameters.hasOwnProperty("fontface") ? parameters["fontface"] : "Arial";
	this.fontsize = parameters.hasOwnProperty("fontsize") ? parameters["fontsize"] : 10;
	this.color = parameters.hasOwnProperty("color") ? {	r: parameters["color"].r, g: parameters["color"].g, b: parameters["color"].b } : { r: 0, g: 0, b: 0 };
	this.opacity = parameters.hasOwnProperty("opacity") ? parameters["opacity"] : 0.5;
	this.textLineThickness = parameters.hasOwnProperty("textLineThickness") ? parameters["textLineThickness"] : 6;

// this.spriteAlignment = THREE.SpriteAlignment.topLeft;

	// create a nested canvas & context
	this.canvas = document.createElement('canvas');
	this.context = this.canvas.getContext('2d');
	this.context.font = "Bold " + this.fontsize + "px " + this.fontface;
	
	// get size data (height depends only on font size)
	this.metrics = this.context.measureText( message );
	this.textWidth = this.metrics.width;
	
	// text color
	this.context.fillStyle = "rgba(" + this.color.r + "," + this.color.g + "," + this.color.b + "," + this.opacity + " )";
	this.context.fillText( message, this.textLineThickness, this.fontsize + this.textLineThickness );
	
	// canvas contents will be used for a texture
	this.texture = new THREE.Texture( this.canvas ); 
	this.texture.needsUpdate = true;
	this.texture.minFilter = THREE.LinearFilter;

	this.spriteMaterial = new THREE.SpriteMaterial( { map: this.texture } );
	this.sprite = new THREE.Sprite( this.spriteMaterial );
	this.sprite.scale.set( globalAppSettings.defaultLabelScale.x, globalAppSettings.defaultLabelScale.y, globalAppSettings.defaultLabelScale.z );
	return this.sprite;	
}

}

File handling utils where I’m trying to save the JSON:

var saveFile = function( url, filename, content ){

var httpRequest = new XMLHttpRequest();

var body = { 
	fullpath: url + filename,  		// filename includes ext
	data: content   				// file contents		
};

var jBody = JSON.stringify( body, circularRefHandler );

// Send the request
httpRequest.open("POST", '/save', true);
httpRequest.setRequestHeader( "Content-Type", "application/json;charset=UTF-8" );
httpRequest.send( jBody );	

console.log( httpRequest );
};

var setFileExt = function( filename ){

return filename.split('.').pop();

};
// FILE SAVING UTILS


// Handling circular references throwing errors in JSON.stringify
var circularRefHandler = function( key, value ) {

  var refs = ( 'node' || 'nodes' || 'edge' || 'edges' || 'label' || 'displayEntity' );
  var returns = ( value.id || null );

  if ( key === refs ) { return returns; }
  //  if ( key !== ( 'node' || 'Node' ) ) { return returns; }
  else {return value;}

};

I hope this gives something to work with on this issue.

Thanks!

I ran into this as well.

From what I’ve learned, it has something to do with maps on materials (normal/bump/etc.) not being serialized correctly.

I also noticed that all textures are serialized using base64, even if those textures are used more than once. However, if you use the same texture across multiple materials, you’re better off writing your own serializer or replacing materials with dummies before serializing your object. It depends on your use case of course.

@Harold, glad to see I’m not the only one and that it’s not something I broke :slight_smile: Hoping from here we can get a master with a workaround in the mix.

Please try this approach:

var objectLoader = new THREE.ObjectLoader();
var object = objectLoader.parse( json );

@Mugen87, the problem isn’t about loading, but rather saving an object.

From what I understand when I came across this problem is that some textures (or images) cannot be serialized into JSON format, like environment maps that are generated by cube cameras - which is quite obvious. However, the UUID reference is stored in one of the texture arrays, but the texture itself isn’t because it’s not a physical image.

I ran into this problem as well. One, oddity I noticed when trying to come up with a workaround is that it only seems to occur for me when I am serializing a plain object containing three.js objects:

image