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

com.metaparadigm.jsonrpc.JSONSerializer Maven / Gradle / Ivy

The newest version!
/*
 * JSON-RPC-Java - a JSON-RPC to Java Bridge with dynamic invocation
 *
 * $Id: JSONSerializer.java,v 1.9.2.3 2006/03/06 12:39:21 mclark Exp $
 *
 * Copyright Metaparadigm Pte. Ltd. 2004.
 * Michael Clark 
 *
 * 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.metaparadigm.jsonrpc;

import java.util.HashSet;
import java.util.HashMap;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.logging.Logger;
import java.text.ParseException;
import java.io.Serializable;
import org.json.JSONObject;
import org.json.JSONArray;
import org.json.JSONTokener;

/**
 * This class is the public entry point to the serialization code and provides
 * methods for marshalling Java objects into JSON objects and unmarshalling
 * JSON objects into Java objects.
 */

public class JSONSerializer implements Serializable
{
    private final static long serialVersionUID = 1;

    private final static Logger log =
	Logger.getLogger(JSONSerializer.class.getName());

    private boolean debug = false;

    public void setDebug(boolean debug) { this.debug = debug; }
    public boolean isDebug() { return debug; }

    private void readObject(java.io.ObjectInputStream in)
        throws java.io.IOException, ClassNotFoundException
    {
        in.defaultReadObject();
        serializableMap = new HashMap();
        Iterator i = serializerList.iterator();
        while(i.hasNext()) {
            Serializer s = (Serializer)i.next();
            Class classes[] = s.getSerializableClasses();
            for (int j = 0; j < classes.length; j++) {
                serializableMap.put(classes[j], s);
            }
        }
    }

    // Key Serializer
    private HashSet serializerSet = new HashSet();
    // key Class, val Serializer
    private transient HashMap serializableMap = new HashMap();
    // List for reverse registration order search
    private ArrayList serializerList = new ArrayList();

    private boolean marshallClassHints = true;
    private boolean marshallNullAttributes = true;
    
    public void registerDefaultSerializers()
	throws Exception
    {
	registerSerializer(new BeanSerializer());
	registerSerializer(new ArraySerializer());
	registerSerializer(new DictionarySerializer());
	registerSerializer(new MapSerializer());
	registerSerializer(new SetSerializer());
	registerSerializer(new ListSerializer());
	registerSerializer(new DateSerializer());
	registerSerializer(new StringSerializer());
	registerSerializer(new NumberSerializer());
	registerSerializer(new BooleanSerializer());
	registerSerializer(new PrimitiveSerializer());
    }

    public void registerSerializer(Serializer s)
	throws Exception
    {
	Class classes[] = s.getSerializableClasses();
	Serializer exists;
	synchronized (serializerSet) {
	    for(int i=0; i < classes.length; i++) {
		exists = (Serializer)serializableMap.get(classes[i]);
		if(exists != null && exists.getClass() != s.getClass())
		    throw new Exception
			("different serializer already registered for " +
			 classes[i].getName());
	    }
	    if(!serializerSet.contains(s)) {
		if(isDebug())
		    log.info("registered serializer " +
			     s.getClass().getName());
		for(int i=0; i < classes.length; i++) {
		    serializableMap.put(classes[i], s);
		}
		s.setOwner(this);
		serializerSet.add(s);
		serializerList.add(0, s);
	    }
	}
    }

    private Serializer getSerializer(Class clazz, Class jsoClazz)
    {
	if(isDebug())
	    log.fine("looking for serializer - java:" +
		     (clazz == null ? "null" : clazz.getName()) +
		     " json:" +
		     (jsoClazz == null ? "null" : jsoClazz.getName()));

	Serializer s = null;
	synchronized (serializerSet) {
	    s = (Serializer)serializableMap.get(clazz);
	    if(s != null && s.canSerialize(clazz, jsoClazz)) {
		if(isDebug())
		    log.fine("direct match serializer " +
			     s.getClass().getName());
		return s;
	    }
	    Iterator i = serializerList.iterator();
	    while(i.hasNext()) {
		s = (Serializer)i.next();
		if(s.canSerialize(clazz, jsoClazz)) {
		    if(isDebug())
			log.fine("search found serializer " +
				 s.getClass().getName()); 
		    return s;
		}
	    }
	}
	return null;
    }

    private Class getClassFromHint(Object o)
	throws UnmarshallException
    {
	if(o == null) return null;
	if(o instanceof JSONObject) {
	    try {
		String class_name = ((JSONObject)o).getString("javaClass");
		Class clazz = Class.forName(class_name);
		return clazz;
	    } catch (NoSuchElementException e) {
	    } catch (Exception e) {
		throw new UnmarshallException("class in hint not found");
	    }
	}
	if(o instanceof JSONArray) {
	    JSONArray arr = (JSONArray)o;
	    if(arr.length() == 0)
		throw new UnmarshallException("no type for empty array");
	    // return type of first element
	    Class compClazz = getClassFromHint(arr.get(0));
	    try {
		if(compClazz.isArray())
		    return Class.forName("[" + compClazz.getName());
		else
		    return Class.forName("[L" + compClazz.getName() + ";");
	    } catch (ClassNotFoundException e) {
		throw new UnmarshallException("problem getting array type");
	    }
	}
	return o.getClass();
    }

    public ObjectMatch tryUnmarshall(SerializerState state,
				     Class clazz, Object json)
	throws UnmarshallException
    {
	/* If we have a JSON object class hint that is a sub class of the
	   signature 'clazz', then override 'clazz' with the hint class. */
	if(clazz != null &&
	   json instanceof JSONObject &&
	   ((JSONObject)json).has("javaClass") &&
	   clazz.isAssignableFrom(getClassFromHint(json)))
	    clazz = getClassFromHint(json);

	if(clazz == null)
	    clazz = getClassFromHint(json);
	if(clazz == null)
	    throw new UnmarshallException("no class hint");
	if(json == null || json == JSONObject.NULL) {
	    if(!clazz.isPrimitive())
		return ObjectMatch.NULL;
	    else
		throw new UnmarshallException("can't assign null primitive");
	}
	Serializer s = getSerializer(clazz, json.getClass());
	if(s != null) return s.tryUnmarshall(state, clazz, json);

	throw new UnmarshallException("no match");
    }

    public Object unmarshall(SerializerState state, Class clazz, Object json)
	throws UnmarshallException
    {
	/* If we have a JSON object class hint that is a sub class of the
	   signature 'clazz', then override 'clazz' with the hint class. */
	if(clazz != null &&
	   json instanceof JSONObject &&
	   ((JSONObject)json).has("javaClass") &&
	   clazz.isAssignableFrom(getClassFromHint(json)))
	    clazz = getClassFromHint(json);

	if(clazz == null)
	    clazz = getClassFromHint(json);
	if(clazz == null)
	    throw new UnmarshallException("no class hint");
	if(json == null || json == JSONObject.NULL) {
	    if(!clazz.isPrimitive())
		return null;
	    else
		throw new UnmarshallException("can't assign null primitive");
	}
	Serializer s = getSerializer(clazz, json.getClass());
	if(s != null) return s.unmarshall(state, clazz, json);

	throw new UnmarshallException("can't unmarshall");
    }

    public Object marshall(SerializerState state, Object o)
	throws MarshallException
    {
	if(o == null) {
	    if(isDebug()) log.fine("marshall null");
	    return JSONObject.NULL;
	}
	if(isDebug()) log.fine("marshall class " + o.getClass().getName());
	Serializer s = getSerializer(o.getClass(), null);
	if(s != null) return s.marshall(state, o);
	throw new MarshallException("can't marshall " +
				    o.getClass().getName());
    }

    public String toJSON(Object o)
	throws MarshallException
    {
	SerializerState state = new SerializerState();
	Object json = marshall(state, o);
	return json.toString();
    }

    public Object fromJSON(String s)
	throws UnmarshallException
    {
	JSONTokener tok =   new JSONTokener(s);
	Object json;
	try {
	    json = tok.nextValue();
	} catch(ParseException e) {
	    throw new UnmarshallException("couldn't parse JSON");
	}
	SerializerState state = new SerializerState();
	return unmarshall(state, null, json);
    }

    /**
     * Should serializers defined in this object include the fully 
     * qualified class name of objects being serialized?  This can 
     * be helpful when unmarshalling, though if not needed can
     * be left out in favor of increased performance and smaller 
     * size of marshalled String.  Default is true.
     *
     * @return whether Java Class hints are included in the serialised
     *         JSON objects
     */
    public boolean getMarshallClassHints() {
        return marshallClassHints;
    }

    /**
     * Should serializers defined in this object include the fully 
     * qualified class name of objects being serialized?  This can 
     * be helpful when unmarshalling, though if not needed can
     * be left out in favor of increased performance and smaller 
     * size of marshalled String.  Default is true.
     *
     * @param marshallClassHints flag to enable/disable inclusion
     *        of Java class hints in the serialized JSON objects
     */
    public void setMarshallClassHints(boolean marshallClassHints) {
        this.marshallClassHints = marshallClassHints;
    }
    
    /** 
     * Returns true if attributes will null values should still be included
     * in the serialized JSON object.  Defaults to true.  Set to false for 
     * performance gains and small JSON serialized size.  Useful because null and 
     * undefined for JSON object attributes is virtually the same thing.
     *
     * @return boolean value as to whether null attributes will be
     *         in the serialized JSON objects
     */
    public boolean getMarshallNullAttributes() {
        return marshallNullAttributes;
    }
    /** 
     * Returns true if attributes will null values should still be included
     * in the serialized JSON object.  Defaults to true.  Set to false for 
     * performance gains and small JSON serialized size.  Useful because null and 
     * undefined for JSON object attributes is virtually the same thing.
     *
     * @param marshallNullAttributes flag to enable/disable marshalling of
     *        null attributes in the serialized JSON objects
     */
    public void setMarshallNullAttributes(boolean marshallNullAttributes) {
        this.marshallNullAttributes = marshallNullAttributes;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy