working on serializer refactoring.
diff --git a/build.xml b/build.xml
index dfefb44..e8b4181 100644
--- a/build.xml
+++ b/build.xml
@@ -207,12 +207,14 @@
,org.codehaus.jackson.io;version=${IMPL_VERSION}
,org.codehaus.jackson.map;version=${IMPL_VERSION}
,org.codehaus.jackson.map.node;version=${IMPL_VERSION}
+,org.codehaus.jackson.map.ser;version=${IMPL_VERSION}
'
privatePackage="!test
,!org.codehaus.jackson
,!org.codehaus.jackson.io
,!org.codehaus.jackson.map
,!org.codehaus.jackson.map.node
+,!org.codehaus.jackson.map.ser
,*"
includeResource="META-INF=release-notes/asl"
includeIncludeResourceHeader="false"
diff --git a/src/java/org/codehaus/jackson/map/JsonSerializer.java b/src/java/org/codehaus/jackson/map/JsonSerializer.java
index 88d8053..257d2aa 100644
--- a/src/java/org/codehaus/jackson/map/JsonSerializer.java
+++ b/src/java/org/codehaus/jackson/map/JsonSerializer.java
@@ -16,7 +16,7 @@
* values of type this serializer handles.
*
* @param value Value to serialize
- * @param gen Generator used to output resulting Json content
+ * @param jgen Generator used to output resulting Json content
* @param provider Provider that can be used to get serializers for
* serializing Objects value contains, if any.
*/
diff --git a/src/java/org/codehaus/jackson/map/JsonSerializerFactory.java b/src/java/org/codehaus/jackson/map/JsonSerializerFactory.java
index 6e600ae..623bb36 100644
--- a/src/java/org/codehaus/jackson/map/JsonSerializerFactory.java
+++ b/src/java/org/codehaus/jackson/map/JsonSerializerFactory.java
@@ -13,4 +13,3 @@
*/
public abstract <T> JsonSerializer<T> createSerializer(Class<T> type);
}
-
diff --git a/src/java/org/codehaus/jackson/map/JsonSerializerProvider.java b/src/java/org/codehaus/jackson/map/JsonSerializerProvider.java
index 007616c..6d8d101 100644
--- a/src/java/org/codehaus/jackson/map/JsonSerializerProvider.java
+++ b/src/java/org/codehaus/jackson/map/JsonSerializerProvider.java
@@ -32,14 +32,15 @@
/**
* Method called to get the serializer to use for serializing
- * Map keys that are not nulls (for null keys,
- * {@link #getNullKeySerializer} is called instead).
- * Separation from regular
- * {@link #findSerializer} method is due to Json only allowing
- * Json Strings as field names; and hence different serialization
- * strategy may be needed.
+ * non-null Map keys. Separation from regular
+ * {@link #findValueSerializer} method is because actual write
+ * method must be different (@link JsonGenerator#writeFieldName};
+ * but also since behavior for some key types may differ.
+ *<p>
+ * Note that the serializer itself can be called with instances
+ * of any Java object, but not nulls.
*/
- public abstract JsonSerializer<?> findNonNullKeySerializer(Class<?> type);
+ public abstract JsonSerializer<Object> getKeySerializer();
/**
* Method called to get the serializer to use for serializing
@@ -50,7 +51,7 @@
* will either throw an exception, or use an empty String; but
* other behaviors are possible.
*/
- public abstract JsonSerializer<?> getNullKeySerializer();
+ public abstract JsonSerializer<Object> getNullKeySerializer();
/**
* Method called to get the serializer to use for serializing
@@ -61,5 +62,5 @@
* Typically returned serializer just writes out Json literal
* null value.
*/
- public abstract JsonSerializer<?> getNullValueSerializer();
+ public abstract JsonSerializer<Object> getNullValueSerializer();
}
diff --git a/src/java/org/codehaus/jackson/map/ser/BeanSerializerFactory.java b/src/java/org/codehaus/jackson/map/ser/BeanSerializerFactory.java
index fe0ac2d..14c0e30 100644
--- a/src/java/org/codehaus/jackson/map/ser/BeanSerializerFactory.java
+++ b/src/java/org/codehaus/jackson/map/ser/BeanSerializerFactory.java
@@ -1,13 +1,6 @@
package org.codehaus.jackson.map.ser;
-import java.io.IOException;
-import java.util.*;
-
-import org.codehaus.jackson.JsonGenerationException;
-import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.map.JsonSerializer;
-import org.codehaus.jackson.map.JsonSerializerFactory;
-import org.codehaus.jackson.map.JsonSerializerProvider;
/**
* Factory class that can provide serializers for any regular Java beans
diff --git a/src/java/org/codehaus/jackson/map/ser/FailingSerializer.java b/src/java/org/codehaus/jackson/map/ser/FailingSerializer.java
new file mode 100644
index 0000000..c3e2dda
--- /dev/null
+++ b/src/java/org/codehaus/jackson/map/ser/FailingSerializer.java
@@ -0,0 +1,29 @@
+package org.codehaus.jackson.map.ser;
+
+import java.io.IOException;
+
+import org.codehaus.jackson.JsonGenerationException;
+import org.codehaus.jackson.JsonGenerator;
+import org.codehaus.jackson.map.JsonSerializer;
+import org.codehaus.jackson.map.JsonSerializerProvider;
+
+/**
+ * Special bogus "serializer" that will throw
+ * {@link JsonGenerationException} if its {@link #serialize}
+ * gets invoeked. Most commonly registered as handler for unknown types,
+ * as well as for catching unintended usage (like trying to use null
+ * as Map/Object key).
+ */
+public final class FailingSerializer
+ extends JsonSerializer<Object>
+{
+ final String _msg;
+
+ public FailingSerializer(String msg) { _msg = msg; }
+
+ public void serialize(Object value, JsonGenerator jgen, JsonSerializerProvider provider)
+ throws IOException, JsonGenerationException
+ {
+ throw new JsonGenerationException(_msg);
+ }
+}
diff --git a/src/java/org/codehaus/jackson/map/ser/SerializerProviderBase.java b/src/java/org/codehaus/jackson/map/ser/SerializerProviderBase.java
index 52b8923..6bba0b4 100644
--- a/src/java/org/codehaus/jackson/map/ser/SerializerProviderBase.java
+++ b/src/java/org/codehaus/jackson/map/ser/SerializerProviderBase.java
@@ -1,14 +1,13 @@
package org.codehaus.jackson.map.ser;
-import java.util.*;
-import java.util.concurrent.ConcurrentHashMap;
+//import java.util.concurrent.ConcurrentHashMap;
import org.codehaus.jackson.map.JsonSerializer;
import org.codehaus.jackson.map.JsonSerializerProvider;
/**
* Abstract base class that defines lowest-level common feature of most
- * implementations of {@JsonSerializerProvider}: that of locally
+ * implementations of {@link JsonSerializerProvider}: that of locally
* storing references to serializers once they have been succesfully
* constructed.
*/
@@ -24,13 +23,13 @@
}
@Override
- public abstract JsonSerializer<?> findNonNullKeySerializer(Class<?> type);
+ public abstract JsonSerializer<Object> getKeySerializer();
@Override
- public abstract JsonSerializer<?> getNullKeySerializer();
+ public abstract JsonSerializer<Object> getNullKeySerializer();
@Override
- public abstract JsonSerializer<?> getNullValueSerializer();
+ public abstract JsonSerializer<Object> getNullValueSerializer();
/*
//////////////////////////////////////////////////
diff --git a/src/java/org/codehaus/jackson/map/ser/StdKeySerializer.java b/src/java/org/codehaus/jackson/map/ser/StdKeySerializer.java
new file mode 100644
index 0000000..0c524ea
--- /dev/null
+++ b/src/java/org/codehaus/jackson/map/ser/StdKeySerializer.java
@@ -0,0 +1,27 @@
+package org.codehaus.jackson.map.ser;
+
+import java.io.IOException;
+
+import org.codehaus.jackson.JsonGenerationException;
+import org.codehaus.jackson.JsonGenerator;
+import org.codehaus.jackson.map.JsonSerializer;
+import org.codehaus.jackson.map.JsonSerializerProvider;
+
+/**
+ * Specialized serializer that can be used as the generic key
+ * serializer, when serializing {@link java.util.Map}s to Json
+ * Objects.
+ */
+public final class StdKeySerializer
+ extends JsonSerializer<Object>
+{
+ final static StdKeySerializer instace = new StdKeySerializer();
+
+ public void serialize(Object value, JsonGenerator jgen, JsonSerializerProvider provider)
+ throws IOException, JsonGenerationException
+ {
+ String keyStr = (value.getClass() == String.class) ?
+ ((String) value) : value.toString();
+ jgen.writeFieldName(keyStr);
+ }
+}
diff --git a/src/java/org/codehaus/jackson/map/ser/StdSerializerFactory.java b/src/java/org/codehaus/jackson/map/ser/StdSerializerFactory.java
index 9e2c288..60988fb 100644
--- a/src/java/org/codehaus/jackson/map/ser/StdSerializerFactory.java
+++ b/src/java/org/codehaus/jackson/map/ser/StdSerializerFactory.java
@@ -39,11 +39,12 @@
// String and string-like types (note: date types explicitly
// not included -- can use either textual or numeric serialization)
_concrete.put(String.class.getName(), new StringSerializer());
- _concrete.put(StringBuffer.class.getName(), StringLikeSerializer.instance);
- _concrete.put(StringBuilder.class.getName(), StringLikeSerializer.instance);
- _concrete.put(Character.class.getName(), StringLikeSerializer.instance);
+ ToStringSerializer sls = ToStringSerializer.instance;
+ _concrete.put(StringBuffer.class.getName(), sls);
+ _concrete.put(StringBuilder.class.getName(), sls);
+ _concrete.put(Character.class.getName(), sls);
// including things best serialized as Strings
- _concrete.put(UUID.class.getName(), StringLikeSerializer.instance);
+ _concrete.put(UUID.class.getName(), sls);
// Numbers, limited length integral
final IntegerSerializer intS = new IntegerSerializer();
@@ -89,13 +90,16 @@
_concrete.put(Hashtable.class.getName(), mapS);
_concrete.put(LinkedHashMap.class.getName(), mapS);
_concrete.put(TreeMap.class.getName(), mapS);
- _concrete.put(EnumMap.class.getName(), mapS);
_concrete.put(Properties.class.getName(), mapS);
_concrete.put(HashSet.class.getName(), collectionS);
_concrete.put(LinkedHashSet.class.getName(), collectionS);
_concrete.put(TreeSet.class.getName(), collectionS);
+ // and Enum-variations of set/map
+ _concrete.put(EnumMap.class.getName(), new EnumMapSerializer());
+ _concrete.put(EnumSet.class.getName(), new EnumSetSerializer());
+
/* Finally, couple of oddball types. Not sure if these are
* really needed...
*/
@@ -206,12 +210,15 @@
}
return CollectionSerializer.instance;
}
- if (Collection.class.isAssignableFrom(type)) {
- return CollectionSerializer.instance;
- }
if (Number.class.isAssignableFrom(type)) {
return NumberSerializer.instance;
}
+ if (Enum.class.isAssignableFrom(type)) {
+ return new EnumSerializer();
+ }
+ if (Collection.class.isAssignableFrom(type)) {
+ return CollectionSerializer.instance;
+ }
return null;
}
@@ -514,6 +521,22 @@
}
}
+ public final static class EnumSetSerializer
+ extends JsonSerializer<EnumSet<? extends Enum<?>>>
+ {
+ public final static CollectionSerializer instance = new CollectionSerializer();
+
+ public void serialize(EnumSet<? extends Enum<?>> value, JsonGenerator jgen, JsonSerializerProvider provider)
+ throws IOException, JsonGenerationException
+ {
+ jgen.writeStartArray();
+ for (Enum<?> en : value) {
+ jgen.writeString(en.name());
+ }
+ jgen.writeEndArray();
+ }
+ }
+
/*
////////////////////////////////////////////////////////////
// Concrete serializers, Maps
@@ -534,9 +557,8 @@
final int len = value.size();
if (len > 0) {
- JsonSerializer<Object> prevKeySerializer = null;
+ final JsonSerializer<Object> keySerializer = provider.getKeySerializer();
JsonSerializer<Object> prevValueSerializer = null;
- Class<?> prevKeyClass = null;
Class<?> prevValueClass = null;
for (Map.Entry<?,?> entry : value.entrySet()) {
@@ -545,16 +567,7 @@
if (keyElem == null) {
provider.getNullKeySerializer().serialize(null, jgen, provider);
} else {
- Class<?> cc = keyElem.getClass();
- JsonSerializer<Object> currSerializer;
- if (cc == prevKeyClass) {
- currSerializer = prevKeySerializer;
- } else {
- currSerializer = (JsonSerializer<Object>)provider.findNonNullKeySerializer(cc);
- prevKeySerializer = currSerializer;
- prevKeyClass = cc;
- }
- currSerializer.serialize(keyElem, jgen, provider);
+ keySerializer.serialize(keyElem, jgen, provider);
}
// And then value
@@ -580,6 +593,42 @@
}
}
+ public final static class EnumMapSerializer
+ extends JsonSerializer<EnumMap<? extends Enum<?>, ?>>
+ {
+ @SuppressWarnings("unchecked")
+ public void serialize(EnumMap<? extends Enum<?>,?> value, JsonGenerator jgen, JsonSerializerProvider provider)
+ throws IOException, JsonGenerationException
+ {
+ jgen.writeStartArray();
+ JsonSerializer<Object> prevSerializer = null;
+ Class<?> prevClass = null;
+
+ for (Map.Entry<? extends Enum<?>,?> entry : value.entrySet()) {
+ // First, serialize key
+ jgen.writeFieldName(entry.getKey().name());
+ // And then value
+ Object valueElem = entry.getKey();
+ if (valueElem == null) {
+ provider.getNullValueSerializer().serialize(null, jgen, provider);
+ } else {
+ Class<?> cc = valueElem.getClass();
+ JsonSerializer<Object> currSerializer;
+ if (cc == prevClass) {
+ currSerializer = prevSerializer;
+ } else {
+ currSerializer = (JsonSerializer<Object>)provider.findValueSerializer(cc);
+ prevSerializer = currSerializer;
+ prevClass = cc;
+ }
+ currSerializer.serialize(valueElem, jgen, provider);
+ }
+ }
+
+ jgen.writeEndArray();
+ }
+ }
+
/*
////////////////////////////////////////////////////////////
// Concrete serializers, arrays
@@ -765,10 +814,20 @@
/*
////////////////////////////////////////////////////////////
- // Other odd-ball special purpose serializers
+ // Other odd-ball special-purpose serializers
////////////////////////////////////////////////////////////
*/
+ public final static class EnumSerializer
+ extends JsonSerializer<Enum<?>>
+ {
+ public void serialize(Enum<?> value, JsonGenerator jgen, JsonSerializerProvider provider)
+ throws IOException, JsonGenerationException
+ {
+ jgen.writeString(value.name());
+ }
+ }
+
/**
* To allow for special handling for null values (in Objects, Arrays,
* root-level), handling for nulls is done via serializers too.
diff --git a/src/java/org/codehaus/jackson/map/ser/StdSerializerProvider.java b/src/java/org/codehaus/jackson/map/ser/StdSerializerProvider.java
index 8da4729..163f340 100644
--- a/src/java/org/codehaus/jackson/map/ser/StdSerializerProvider.java
+++ b/src/java/org/codehaus/jackson/map/ser/StdSerializerProvider.java
@@ -1,8 +1,5 @@
package org.codehaus.jackson.map.ser;
-import java.io.IOException;
-
-import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.map.JsonSerializer;
@@ -20,12 +17,14 @@
public class StdSerializerProvider
extends SerializerProviderBase
{
- public final static JsonSerializer<?> DEFAULT_NULL_KEY_SERIALIZER =
+ public final static JsonSerializer<Object> DEFAULT_NULL_KEY_SERIALIZER =
new StdSerializerFactory.FailingSerializer("Null key for a Map not allower in Json (use a converting NullKeySerializer?)");
+ public final static JsonSerializer<Object> DEFAULT_KEY_SERIALIZER = new StdKeySerializer();
+
/*
////////////////////////////////////////////////////
- // Configuration
+ // Configuration, factories
////////////////////////////////////////////////////
*/
@@ -41,20 +40,42 @@
*/
protected JsonSerializerFactory _overrideSerializerFactory = null;
+ /*
+ ////////////////////////////////////////////////////
+ // Configuration, specialized serializers
+ ////////////////////////////////////////////////////
+ */
+
+ /**
+ * Serializer that gets called for values of types for which no
+ * serializers can be constructed.
+ *<p>
+ * The default serializer will thrown an exception; a possible
+ * alternative that can be used would be
+ * {@link ToStringSerializer}.
+ */
+ protected JsonSerializer<Object> _unknownTypeSerializer;
+
+ /**
+ * Serializer used to output non-null keys of Maps (which will get
+ * output as Json Objects).
+ */
+ protected JsonSerializer<Object> _keySerializer = DEFAULT_KEY_SERIALIZER;
+
/**
* Serializer used to output a null value. Default implementation
* writes nulls using {@link JsonGenerator#writeNull}.
*/
- protected JsonSerializer<?> _nullValueSerializer = StdSerializerFactory.NullSerializer.instance;
+ protected JsonSerializer<Object> _nullValueSerializer = StdSerializerFactory.NullSerializer.instance;
/**
* Serializer used to (try to) output a null key, due to an entry of
- * {@link Map} having null key.
+ * {@link java.util.Map} having null key.
* The default implementation will throw an exception if this happens;
* alternative implementation (like one that would write an Empty String)
* can be defined.
*/
- protected JsonSerializer<?> _nullKeySerializer = DEFAULT_NULL_KEY_SERIALIZER;
+ protected JsonSerializer<Object> _nullKeySerializer = DEFAULT_NULL_KEY_SERIALIZER;
/*
////////////////////////////////////////////////////
@@ -86,7 +107,15 @@
_overrideSerializerFactory = jf;
}
- public void setNullValueSerializer(JsonSerializer<?> nvs)
+ public void setKeySerializer(JsonSerializer<Object> ks)
+ {
+ if (ks == null) {
+ throw new IllegalArgumentException("Can not pass null JsonSerializer");
+ }
+ _keySerializer = ks;
+ }
+
+ public void setNullValueSerializer(JsonSerializer<Object> nvs)
{
if (nvs == null) {
throw new IllegalArgumentException("Can not pass null JsonSerializer");
@@ -94,7 +123,7 @@
_nullValueSerializer = nvs;
}
- public void setNullKeySerializer(JsonSerializer<?> nks)
+ public void setNullKeySerializer(JsonSerializer<Object> nks)
{
if (nks == null) {
throw new IllegalArgumentException("Can not pass null JsonSerializer");
@@ -111,24 +140,35 @@
@Override
protected JsonSerializer<?> constructValueSerializer(Class<?> type)
{
- // !!! TBI
- return null;
+ if (_overrideSerializerFactory != null) {
+ JsonSerializer<?> ser = _overrideSerializerFactory.createSerializer(type);
+ if (ser != null) {
+ return ser;
+ }
+ }
+ JsonSerializer<?> ser = _serializerFactory.createSerializer(type);
+ if (ser != null) {
+ return ser;
+ }
+ /* No match yet? Need to return the fallback serializer, which
+ * most likely will report an error
+ */
+ return _unknownTypeSerializer;
}
@Override
- public final JsonSerializer<?> findNonNullKeySerializer(Class<?> type)
+ public final JsonSerializer<Object> getKeySerializer()
{
- // !!! TBI
- return null;
+ return _keySerializer;
}
@Override
- public final JsonSerializer<?> getNullKeySerializer() {
+ public final JsonSerializer<Object> getNullKeySerializer() {
return _nullKeySerializer;
}
@Override
- public final JsonSerializer<?> getNullValueSerializer() {
+ public final JsonSerializer<Object> getNullValueSerializer() {
return _nullValueSerializer;
}
}
diff --git a/src/java/org/codehaus/jackson/map/ser/ToStringSerializer.java b/src/java/org/codehaus/jackson/map/ser/ToStringSerializer.java
new file mode 100644
index 0000000..8186607
--- /dev/null
+++ b/src/java/org/codehaus/jackson/map/ser/ToStringSerializer.java
@@ -0,0 +1,30 @@
+package org.codehaus.jackson.map.ser;
+
+import java.io.IOException;
+
+import org.codehaus.jackson.JsonGenerationException;
+import org.codehaus.jackson.JsonGenerator;
+import org.codehaus.jackson.map.JsonSerializer;
+import org.codehaus.jackson.map.JsonSerializerProvider;
+
+/**
+ * Simple general purpose serializer, useful for any
+ * type for which {@link Object#toString} returns the desired Json
+ * value.
+ */
+public final class ToStringSerializer
+ extends JsonSerializer<Object>
+{
+ /**
+ * Singleton instance to use.
+ */
+ public final static ToStringSerializer instance = new ToStringSerializer();
+
+ private ToStringSerializer() { } // no instantiation, use singleton
+
+ public void serialize(Object value, JsonGenerator jgen, JsonSerializerProvider provider)
+ throws IOException, JsonGenerationException
+ {
+ jgen.writeString(value.toString());
+ }
+}