All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.antiaction.common.json.JSONStreamMarshaller Maven / Gradle / Ivy

There is a newer version: 0.7.0
Show newest version
/*
 * JSON library.
 * Copyright 2012-2013 Antiaction (http://antiaction.com/)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.antiaction.common.json;

import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Array;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Map;

/**
 * Serialize Java Object(s) into a JSON data stream.
 *
 * @author Nicholas
 * Created on 17/10/2013
 */
public class JSONStreamMarshaller {

	private static final int S_OBJECT_BEGIN = 1;

	private static final int S_OBJECT_END = 2;

	private static final int S_ARRAY_BEGIN = 3;

	private static final int S_ARRAY_END = 4;

	private static final int S_OBJECT = 5;

	private static final int S_ARRAY = 6;

	/** Null string cached as bytes. */
	protected static final byte[] nullBytes = "null".getBytes();

	/** True string cached as bytes. */
	protected static final byte[] trueBytes = "true".getBytes();

	/** False string cached as bytes. */
	protected static final byte[] falseBytes = "false".getBytes();

	protected byte[] indentationArr;

	protected JSONObjectMappings objectMappings;

	protected Map classMappings;

	private static final class StackEntry {
		Object object;
		JSONObjectMapping objectMapping;
		int state;
		//int indentation;
		JSONObjectFieldMapping[] fieldMappingsArr;
		int fieldMappingIdx;
		//JSONObjectFieldMapping fieldMapping;
		int len;
	}

	public JSONStreamMarshaller(JSONObjectMappings objectMappings) {
		this.objectMappings = objectMappings;
		this.classMappings = objectMappings.classMappings;
		indentationArr = new byte[ 32 ];
		Arrays.fill( indentationArr, (byte)' ' );
	}

	public  void toJSON(T srcObj, JSONEncoder encoder, boolean bPretty, OutputStream out) throws IOException, JSONException {
		toJSON( srcObj, null, encoder, bPretty, out );
	}

	public  void toJSON(T srcObj, JSONConverterAbstract[] converters, JSONEncoder encoder, boolean bPretty, OutputStream out) throws IOException, JSONException {
		Boolean booleanVal;
		Integer intVal;
		Long longVal;
		Float floatVal;
		Double doubleVal;
		BigInteger bigIntegerVal;
		BigDecimal bigDecimalVal;
		String stringVal;
		byte[] byteArray;
		Object objectVal;

		Object array_object;
		boolean[] arrayOf_boolean;
		int[] arrayOf_int;
		long[] arrayOf_long;
		float[] arrayOf_float;
		double[] arrayOf_double;
		Boolean[] arrayOf_Boolean;
		Integer[] arrayOf_Integer;
		Long[] arrayOf_Long;
		Float[] arrayOf_Float;
		Double[] arrayOf_Double;
		BigInteger[] arrayOf_BigInteger;
		BigDecimal[] arrayOf_BigDecimal;
		String[] arrayOf_String;
		Object[] arrayOf_Object;

		encoder.init( out );

		Object object = srcObj;
		JSONObjectMapping objectMapping = classMappings.get( object.getClass().getName() );
		if ( objectMapping == null ) {
			throw new IllegalArgumentException( "Class '" + object.getClass().getName() + "' not registered." );
		}
		if ( objectMapping.converters == true && converters == null ) {
			throw new JSONException( "Class '" + object.getClass().getName() + "' may required converters!" );
		}

		int state = S_OBJECT_BEGIN;
		int indentation = 0;
		JSONObjectFieldMapping[] fieldMappingsArr = null;
		int fieldMappingIdx = 0;
		JSONObjectFieldMapping fieldMapping;
		int len = 0;

		LinkedList stack = new LinkedList();
		StackEntry stackEntry;

		boolean bLoop = true;
		boolean bFieldLoop;
		try {
			while ( bLoop ) {
				switch ( state ) {
				case S_OBJECT_BEGIN:
					if ( bPretty ) {
						encoder.write( "{\n" );
						indentation += 2;
						if ( indentation > indentationArr.length ) {
							byte[] newIndentationArr = new byte[ indentationArr.length * 2 ];
							Arrays.fill( newIndentationArr, (byte)' ' );
							indentationArr = newIndentationArr;
						}
					}
					else {
						encoder.write( '{' );
					}
					fieldMappingsArr = objectMapping.fieldMappingsArr;
					fieldMappingIdx = 0;
					state = S_OBJECT;
					break;
				case S_OBJECT_END:
					if ( bPretty ) {
						indentation -= 2;
						encoder.write( "\n" );
						encoder.write( indentationArr, 0, indentation );
					}
					encoder.write( "}" );
					if ( stack.size() > 0 ) {
						stackEntry = stack.removeLast();
						object = stackEntry.object;
						objectMapping = stackEntry.objectMapping;
						state = stackEntry.state;
						//indentation = stackEntry.indentation;
						fieldMappingsArr = stackEntry.fieldMappingsArr;
						fieldMappingIdx = stackEntry.fieldMappingIdx;
						//fieldMapping = stackEntry.fieldMapping;
						len = stackEntry.len;
					}
					else {
						bLoop = false;
					}
					break;
				case S_ARRAY_BEGIN:
					if ( bPretty ) {
						encoder.write( "[\n" );
						indentation += 2;
						if ( indentation > indentationArr.length ) {
							byte[] newIndentationArr = new byte[ indentationArr.length * 2 ];
							Arrays.fill( newIndentationArr, (byte)' ' );
							indentationArr = newIndentationArr;
						}
					}
					else {
						encoder.write( '[' );
					}
					state = S_ARRAY;
					break;
				case S_ARRAY_END:
					if ( bPretty ) {
						indentation -= 2;
						encoder.write( "\n" );
						encoder.write( indentationArr, 0, indentation );
					}
					encoder.write( ']' );
					break;
				case S_OBJECT:
					bFieldLoop = true;
					while ( bFieldLoop ) {
						if ( fieldMappingIdx < fieldMappingsArr.length ) {
							if ( fieldMappingIdx > 0 ) {
								if ( bPretty ) {
									encoder.write( ",\n" );
								}
								else {
									encoder.write( ',' );
								}
							}
							if ( bPretty ) {
								encoder.write( indentationArr, 0, indentation );
							}
							fieldMapping = fieldMappingsArr[ fieldMappingIdx++ ];
							encoder.write( '"' );
							encoder.write( fieldMapping.jsonName );
							encoder.write( '"' );
							if ( bPretty ) {
								encoder.write( ": " );
							}
							else {
								encoder.write( ':' );
							}
							switch ( fieldMapping.type ) {
							case JSONObjectMappingConstants.T_PRIMITIVE_BOOLEAN:
								booleanVal = fieldMapping.field.getBoolean( object );
								if ( fieldMapping.converterId != -1 ) {
									// TODO
									//booleanVal = converters[ fieldMapping.converterId ].getJSONValue( fieldMapping.fieldName, booleanVal );
									if ( booleanVal == null ) {
										throw new JSONException( "Field '" + fieldMapping.fieldName + "' is primitive and can not be null." );
									}
								}
								if ( booleanVal ) {
									encoder.write( trueBytes );
								}
								else {
									encoder.write( falseBytes );
								}
								break;
							case JSONObjectMappingConstants.T_PRIMITIVE_INTEGER:
								intVal = fieldMapping.field.getInt( object );
								if ( fieldMapping.converterId != -1 ) {
									// TODO
									//intVal = converters[ fieldMapping.converterId ].getJSONValue( fieldMapping.fieldName, intVal );
									if ( intVal == null ) {
										throw new JSONException( "Field '" + fieldMapping.fieldName + "' is primitive and can not be null." );
									}
								}
								encoder.write( intVal.toString().getBytes() );
								break;
							case JSONObjectMappingConstants.T_PRIMITIVE_LONG:
								longVal = fieldMapping.field.getLong( object );
								if ( fieldMapping.converterId != -1 ) {
									// TODO
									//longVal = converters[ fieldMapping.converterId ].getJSONValue( fieldMapping.fieldName, longVal );
									if ( longVal == null ) {
										throw new JSONException( "Field '" + fieldMapping.fieldName + "' is primitive and can not be null." );
									}
								}
								encoder.write( longVal.toString().getBytes() );
								break;
							case JSONObjectMappingConstants.T_PRIMITIVE_FLOAT:
								floatVal = fieldMapping.field.getFloat( object );
								if ( fieldMapping.converterId != -1 ) {
									// TODO
									//floatVal = converters[ fieldMapping.converterId ].getJSONValue( fieldMapping.fieldName, floatVal );
									if ( floatVal == null ) {
										throw new JSONException( "Field '" + fieldMapping.fieldName + "' is primitive and can not be null." );
									}
								}
								encoder.write( floatVal.toString().getBytes() );
								break;
							case JSONObjectMappingConstants.T_PRIMITIVE_DOUBLE:
								doubleVal = fieldMapping.field.getDouble( object );
								if ( fieldMapping.converterId != -1 ) {
									// TODO
									//doubleVal = converters[ fieldMapping.converterId ].getJSONValue( fieldMapping.fieldName, doubleVal );
									if ( doubleVal == null ) {
										throw new JSONException( "Field '" + fieldMapping.fieldName + "' is primitive and can not be null." );
									}
								}
								encoder.write( doubleVal.toString().getBytes() );
								break;
							case JSONObjectMappingConstants.T_BOOLEAN:
								booleanVal = (Boolean)fieldMapping.field.get( object );
								if ( fieldMapping.converterId != -1 ) {
									// TODO
									//booleanVal = converters[ fieldMapping.converterId ].getJSONValue( fieldMapping.fieldName, booleanVal );
								}
								if ( booleanVal == null && !fieldMapping.nullable ) {
									throw new JSONException( "Field '" + fieldMapping.fieldName + "' is not nullable." );
								}
								if ( booleanVal != null ) {
									if ( booleanVal ) {
										encoder.write( trueBytes );
									}
									else {
										encoder.write( falseBytes );
									}
								}
								else {
									encoder.write( nullBytes );
								}
								break;
							case JSONObjectMappingConstants.T_INTEGER:
								intVal = (Integer)fieldMapping.field.get( object );
								if ( fieldMapping.converterId != -1 ) {
									// TODO
									//intVal = converters[ fieldMapping.converterId ].getJSONValue( fieldMapping.fieldName, intVal );
								}
								if ( intVal == null && !fieldMapping.nullable ) {
									throw new JSONException( "Field '" + fieldMapping.fieldName + "' is not nullable." );
								}
								if ( intVal != null) {
									encoder.write( intVal.toString().getBytes() );
								}
								else {
									encoder.write( nullBytes );
								}
								break;
							case JSONObjectMappingConstants.T_LONG:
								longVal = (Long)fieldMapping.field.get( object );
								if ( fieldMapping.converterId != -1 ) {
									// TODO
									//longVal = converters[ fieldMapping.converterId ].getJSONValue( fieldMapping.fieldName, longVal );
								}
								if ( longVal == null && !fieldMapping.nullable ) {
									throw new JSONException( "Field '" + fieldMapping.fieldName + "' is not nullable." );
								}
								if ( longVal != null ) {
									encoder.write( longVal.toString().getBytes() );
								}
								else {
									encoder.write( nullBytes );
								}
								break;
							case JSONObjectMappingConstants.T_FLOAT:
								floatVal = (Float)fieldMapping.field.get( object );
								if ( fieldMapping.converterId != -1 ) {
									// TODO
									//floatVal = converters[ fieldMapping.converterId ].getJSONValue( fieldMapping.fieldName, floatVal );
								}
								if ( floatVal == null && !fieldMapping.nullable ) {
									throw new JSONException( "Field '" + fieldMapping.fieldName + "' is not nullable." );
								}
								if ( floatVal != null ) {
									encoder.write( floatVal.toString().getBytes() );
								}
								else {
									encoder.write( nullBytes );
								}
								break;
							case JSONObjectMappingConstants.T_DOUBLE:
								doubleVal = (Double)fieldMapping.field.get( object );
								if ( fieldMapping.converterId != -1 ) {
									// TODO
									//doubleVal = converters[ fieldMapping.converterId ].getJSONValue( fieldMapping.fieldName, doubleVal );
								}
								if ( doubleVal == null && !fieldMapping.nullable ) {
									throw new JSONException( "Field '" + fieldMapping.fieldName + "' is not nullable." );
								}
								if ( doubleVal != null ) {
									encoder.write( doubleVal.toString().getBytes() );
								}
								else {
									encoder.write( nullBytes );
								}
								break;
							case JSONObjectMappingConstants.T_BIGINTEGER:
								bigIntegerVal = (BigInteger)fieldMapping.field.get( object );
								if ( fieldMapping.converterId != -1 ) {
									// TODO
									//bigIntegerVal = converters[ fieldMapping.converterId ].getJSONValue( fieldMapping.fieldName, bigIntegerVal );
								}
								if ( bigIntegerVal == null && !fieldMapping.nullable ) {
									throw new JSONException( "Field '" + fieldMapping.fieldName + "' is not nullable." );
								}
								if ( bigIntegerVal != null ) {
									encoder.write( bigIntegerVal.toString().getBytes() );
								}
								else {
									encoder.write( nullBytes );
								}
								break;
							case JSONObjectMappingConstants.T_BIGDECIMAL:
								bigDecimalVal = (BigDecimal)fieldMapping.field.get( object );
								if ( fieldMapping.converterId != -1 ) {
									// TODO
									//bigDecimalVal = converters[ fieldMapping.converterId ].getJSONValue( fieldMapping.fieldName, bigDecimalVal );
								}
								if ( bigDecimalVal == null && !fieldMapping.nullable ) {
									throw new JSONException( "Field '" + fieldMapping.fieldName + "' is not nullable." );
								}
								if ( bigDecimalVal != null ) {
									encoder.write( bigDecimalVal.toString().getBytes() );
								}
								else {
									encoder.write( nullBytes );
								}
								break;
							case JSONObjectMappingConstants.T_STRING:
								stringVal = (String)fieldMapping.field.get( object );
								if ( fieldMapping.converterId != -1 ) {
									// TODO
									//strVal = converters[ fieldMapping.converterId ].getJSONValue( fieldMapping.fieldName, strVal );
								}
								if ( stringVal == null && !fieldMapping.nullable ) {
									throw new JSONException( "Field '" + fieldMapping.fieldName + "' is not nullable." );
								}
								if ( stringVal != null ) {
									encoder.write( '"' );
									encoder.write( stringVal );
									encoder.write( '"' );
								}
								else {
									encoder.write( nullBytes );
								}
								break;
							case JSONObjectMappingConstants.T_BYTEARRAY:
								byteArray = (byte[])fieldMapping.field.get( object );
								if ( fieldMapping.converterId != -1 ) {
									// TODO
									//byteArray = converters[ fieldMapping.converterId ].getJSONValue( fieldMapping.fieldName, byteArray );
								}
								if ( byteArray == null && !fieldMapping.nullable ) {
									throw new JSONException( "Field '" + fieldMapping.fieldName + "' is not nullable." );
								}
								if ( byteArray != null ) {
									encoder.write( '"' );
									encoder.write( byteArray );
									encoder.write( '"' );
								}
								else {
									encoder.write( nullBytes );
								}
								break;
							case JSONObjectMappingConstants.T_OBJECT:
								objectVal = (Object)fieldMapping.field.get( object );
								if ( objectVal != null ) {
									// TODO
									//objectVal = toJSON( objectVal, converters );
								}
								else if ( !fieldMapping.nullable ) {
									throw new JSONException( "Field '" + fieldMapping.fieldName + "' is not nullable." );
								}
								if ( objectVal != null ) {
									stackEntry = new StackEntry();
									stackEntry.object = object;
									stackEntry.objectMapping = objectMapping;
									stackEntry.state = S_OBJECT_END;
									//stackEntry.indentation = indentation;
									stackEntry.fieldMappingsArr = fieldMappingsArr;
									stackEntry.fieldMappingIdx = fieldMappingIdx;
									//stackEntry.fieldMapping = fieldMapping;
									len = stackEntry.len;
									stack.add( stackEntry );
									object = objectVal;
									objectMapping = classMappings.get( object.getClass().getName() );
									if ( objectMapping == null ) {
										throw new IllegalArgumentException( "Class '" + object.getClass().getName() + "' not registered." );
									}
									if ( objectMapping.converters == true && converters == null ) {
										throw new JSONException( "Class '" + object.getClass().getName() + "' may required converters!" );
									}
									state = S_OBJECT_BEGIN;
									bFieldLoop = false;
								}
								else {
									encoder.write( nullBytes );
								}
								break;
							case JSONObjectMappingConstants.T_ARRAY:
								array_object = fieldMapping.field.get( object );
								if ( array_object != null ) {
									len = Array.getLength( array_object );
									switch ( fieldMapping.arrayType ) {
									case JSONObjectMappingConstants.T_PRIMITIVE_BOOLEAN:
										arrayOf_boolean = (boolean[])array_object;
										for ( int i=0; i 0 ) {
												encoder.write( ',' );
											}
											if ( booleanVal ) {
												encoder.write( trueBytes );
											}
											else {
												encoder.write( falseBytes );
											}
										}
										break;
									case JSONObjectMappingConstants.T_PRIMITIVE_INTEGER:
										arrayOf_int = (int[])array_object;
										for ( int i=0; i 0 ) {
												encoder.write( ',' );
											}
											if ( booleanVal != null ) {
												if ( booleanVal ) {
													encoder.write( trueBytes );
												}
												else {
													encoder.write( falseBytes );
												}
											}
											else {
												encoder.write( nullBytes );
											}
										}
										break;
									case JSONObjectMappingConstants.T_INTEGER:
										arrayOf_Integer = (Integer[])array_object;
										for ( int i=0; i 0 ) {
												encoder.write( ',' );
											}
											if ( intVal != null ) {
												encoder.write( intVal.toString().getBytes() );
											}
											else {
												encoder.write( nullBytes );
											}
										}
										break;
									case JSONObjectMappingConstants.T_LONG:
										arrayOf_Long = (Long[])array_object;
										for ( int i=0; i 0 ) {
												encoder.write( ',' );
											}
											if ( longVal != null ) {
												encoder.write( longVal.toString().getBytes() );
											}
											else {
												encoder.write( nullBytes );
											}
										}
										break;
									case JSONObjectMappingConstants.T_FLOAT:
										arrayOf_Float = (Float[])array_object;
										for ( int i=0; i 0 ) {
												encoder.write( ',' );
											}
											if ( floatVal != null ) {
												encoder.write( floatVal.toString().getBytes() );
											}
											else {
												encoder.write( nullBytes );
											}
										}
										break;
									case JSONObjectMappingConstants.T_DOUBLE:
										arrayOf_Double = (Double[])array_object;
										for ( int i=0; i 0 ) {
												encoder.write( ',' );
											}
											if ( doubleVal != null ) {
												encoder.write( doubleVal.toString().getBytes() );
											}
											else {
												encoder.write( nullBytes );
											}
										}
										break;
									case JSONObjectMappingConstants.T_BIGINTEGER:
										arrayOf_BigInteger = (BigInteger[])array_object;
										for ( int i=0; i 0 ) {
												encoder.write( ',' );
											}
											if ( bigIntegerVal != null ) {
												encoder.write( bigIntegerVal.toString().getBytes() );
											}
											else {
												encoder.write( nullBytes );
											}
										}
										break;
									case JSONObjectMappingConstants.T_BIGDECIMAL:
										arrayOf_BigDecimal = (BigDecimal[])array_object;
										for ( int i=0; i 0 ) {
												encoder.write( ',' );
											}
											if ( bigDecimalVal != null ) {
												encoder.write( bigDecimalVal.toString().getBytes() );
											}
											else {
												encoder.write( nullBytes );
											}
										}
										break;
									case JSONObjectMappingConstants.T_STRING:
										arrayOf_String = (String[])array_object;
										for ( int i=0; i 0 ) {
												encoder.write( ',' );
											}
											if ( stringVal != null ) {
												encoder.write( '"' );
												encoder.write( stringVal );
												encoder.write( '"' );
											}
											else {
												encoder.write( nullBytes );
											}
										}
										break;
									case JSONObjectMappingConstants.T_OBJECT:
										arrayOf_Object = (Object[])array_object;
										for ( int i=0; i




© 2015 - 2025 Weber Informatics LLC | Privacy Policy