Skip to content
This repository was archived by the owner on Nov 18, 2021. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ classes

# Other
/nbproject/private/
internal
/*.properties
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ apply plugin : 'mattock'
apply plugin : 'nexus'

group = 'com.stanfy'
version = '0.1.8-SNAPSHOT'
version = '0.1.9-SNAPSHOT'

repositories {
mavenCentral()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,292 @@
/*
* Copyright (C) 2011 Google Inc.
*
* 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.google.gson.internal.bind;

import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSyntaxException;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.internal.$Gson$Types;
import com.google.gson.internal.ConstructorConstructor;
import com.google.gson.internal.ObjectConstructor;
import com.google.gson.internal.Streams;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;

import java.io.IOException;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
* Adapts maps to either JSON objects or JSON arrays.
*
* <h3>Maps as JSON objects</h3>
* For primitive keys or when complex map key serialization is not enabled, this
* converts Java {@link Map Maps} to JSON Objects. This requires that map keys
* can be serialized as strings; this is insufficient for some key types. For
* example, consider a map whose keys are points on a grid. The default JSON
* form encodes reasonably: <pre> {@code
* Map<Point, String> original = new LinkedHashMap<Point, String>();
* original.put(new Point(5, 6), "a");
* original.put(new Point(8, 8), "b");
* System.out.println(gson.toJson(original, type));
* }</pre>
* The above code prints this JSON object:<pre> {@code
* {
* "(5,6)": "a",
* "(8,8)": "b"
* }
* }</pre>
* But GSON is unable to deserialize this value because the JSON string name is
* just the {@link Object#toString() toString()} of the map key. Attempting to
* convert the above JSON to an object fails with a parse exception:
* <pre>com.google.gson.JsonParseException: Expecting object found: "(5,6)"
* at com.google.gson.JsonObjectDeserializationVisitor.visitFieldUsingCustomHandler
* at com.google.gson.ObjectNavigator.navigateClassFields
* ...</pre>
*
* <h3>Maps as JSON arrays</h3>
* An alternative approach taken by this type adapter when it is required and
* complex map key serialization is enabled is to encode maps as arrays of map
* entries. Each map entry is a two element array containing a key and a value.
* This approach is more flexible because any type can be used as the map's key;
* not just strings. But it's also less portable because the receiver of such
* JSON must be aware of the map entry convention.
*
* <p>Register this adapter when you are creating your GSON instance.
* <pre> {@code
* Gson gson = new GsonBuilder()
* .registerTypeAdapter(Map.class, new MapAsArrayTypeAdapter())
* .create();
* }</pre>
* This will change the structure of the JSON emitted by the code above. Now we
* get an array. In this case the arrays elements are map entries:
* <pre> {@code
* [
* [
* {
* "x": 5,
* "y": 6
* },
* "a",
* ],
* [
* {
* "x": 8,
* "y": 8
* },
* "b"
* ]
* ]
* }</pre>
* This format will serialize and deserialize just fine as long as this adapter
* is registered.
*/
public final class XmlMapTypeAdapterFactory implements TypeAdapterFactory {
private final ConstructorConstructor constructorConstructor;
final boolean complexMapKeySerialization;
public static final String KEY_NAME = "key";
public static final String VALUE_NAME = "value";

public XmlMapTypeAdapterFactory(ConstructorConstructor constructorConstructor,
boolean complexMapKeySerialization) {
this.constructorConstructor = constructorConstructor;
this.complexMapKeySerialization = complexMapKeySerialization;
}

@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
Type type = typeToken.getType();

Class<? super T> rawType = typeToken.getRawType();
if (!Map.class.isAssignableFrom(rawType)) {
return null;
}

Class<?> rawTypeOfSrc = $Gson$Types.getRawType(type);
Type[] keyAndValueTypes = $Gson$Types.getMapKeyAndValueTypes(type, rawTypeOfSrc);
TypeAdapter<?> keyAdapter = getKeyAdapter(gson, keyAndValueTypes[0]);
TypeAdapter<?> valueAdapter = gson.getAdapter(TypeToken.get(keyAndValueTypes[1]));
ObjectConstructor<T> constructor = constructorConstructor.get(typeToken);

@SuppressWarnings({"unchecked", "rawtypes"})
// we don't define a type parameter for the key or value types
TypeAdapter<T> result = new Adapter(gson, keyAndValueTypes[0], keyAdapter,
keyAndValueTypes[1], valueAdapter, constructor);
return result;
}

/**
* Returns a type adapter that writes the value as a string.
*/
private TypeAdapter<?> getKeyAdapter(Gson context, Type keyType) {
return (keyType == boolean.class || keyType == Boolean.class)
? TypeAdapters.BOOLEAN_AS_STRING
: context.getAdapter(TypeToken.get(keyType));
}

private final class Adapter<K, V> extends TypeAdapter<Map<K, V>> {
private final TypeAdapter<K> keyTypeAdapter;
private final TypeAdapter<V> valueTypeAdapter;
private final ObjectConstructor<? extends Map<K, V>> constructor;

public Adapter(Gson context, Type keyType, TypeAdapter<K> keyTypeAdapter,
Type valueType, TypeAdapter<V> valueTypeAdapter,
ObjectConstructor<? extends Map<K, V>> constructor) {
this.keyTypeAdapter =
new TypeAdapterRuntimeTypeWrapper<K>(context, keyTypeAdapter, keyType);
this.valueTypeAdapter =
new TypeAdapterRuntimeTypeWrapper<V>(context, valueTypeAdapter, valueType);
this.constructor = constructor;
}

@Override
public Map<K, V> read(JsonReader in) throws IOException {
JsonToken peek = in.peek();
if (peek == JsonToken.NULL) {
in.nextNull();
return null;
}

Map<K, V> map = constructor.construct();
in.beginArray();
while (in.hasNext()) {
// in.beginArray(); // entry array
in.beginObject();
K key ;
V value;
String name=in.nextName();
if(VALUE_NAME.equals(name)){
value = valueTypeAdapter.read(in);
in.nextName();
key = keyTypeAdapter.read(in);
}else{
key = keyTypeAdapter.read(in);
in.nextName();
value = valueTypeAdapter.read(in);
}
V replaced = map.put(key, value);
if (replaced != null) {
throw new JsonSyntaxException("duplicate key: " + key);
}
// in.endArray();
in.endObject();;
}
in.endArray();
// if (peek == JsonToken.BEGIN_ARRAY) {
// in.beginArray();
// while (in.hasNext()) {
// in.beginArray(); // entry array
// K key = keyTypeAdapter.read(in);
// V value = valueTypeAdapter.read(in);
// V replaced = map.put(key, value);
// if (replaced != null) {
// throw new JsonSyntaxException("duplicate key: " + key);
// }
// in.endArray();
// }
// in.endArray();
// } else {
// in.beginObject();
// while (in.hasNext()) {
// JsonReaderInternalAccess.INSTANCE.promoteNameToValue(in);
// K key = keyTypeAdapter.read(in);
// V value = valueTypeAdapter.read(in);
// V replaced = map.put(key, value);
// if (replaced != null) {
// throw new JsonSyntaxException("duplicate key: " + key);
// }
// }
// in.endObject();
// }
return map;
}

@Override
public void write(JsonWriter out, Map<K, V> map) throws IOException {
if (map == null) {
out.nullValue();
return;
}

if (!complexMapKeySerialization) {
out.beginObject();
for (Map.Entry<K, V> entry : map.entrySet()) {
out.name(String.valueOf(entry.getKey()));
valueTypeAdapter.write(out, entry.getValue());
}
out.endObject();
return;
}

boolean hasComplexKeys = false;
List<JsonElement> keys = new ArrayList<JsonElement>(map.size());

List<V> values = new ArrayList<V>(map.size());
for (Map.Entry<K, V> entry : map.entrySet()) {
JsonElement keyElement = keyTypeAdapter.toJsonTree(entry.getKey());
keys.add(keyElement);
values.add(entry.getValue());
hasComplexKeys |= keyElement.isJsonArray() || keyElement.isJsonObject();
}

if (hasComplexKeys) {
out.beginArray();
for (int i = 0; i < keys.size(); i++) {
out.beginArray(); // entry array
Streams.write(keys.get(i), out);
valueTypeAdapter.write(out, values.get(i));
out.endArray();
}
out.endArray();
} else {
out.beginObject();
for (int i = 0; i < keys.size(); i++) {
JsonElement keyElement = keys.get(i);
out.name(keyToString(keyElement));
valueTypeAdapter.write(out, values.get(i));
}
out.endObject();
}
}

private String keyToString(JsonElement keyElement) {
if (keyElement.isJsonPrimitive()) {
JsonPrimitive primitive = keyElement.getAsJsonPrimitive();
if (primitive.isNumber()) {
return String.valueOf(primitive.getAsNumber());
} else if (primitive.isBoolean()) {
return Boolean.toString(primitive.getAsBoolean());
} else if (primitive.isString()) {
return primitive.getAsString();
} else {
throw new AssertionError();
}
} else if (keyElement.isJsonNull()) {
return "null";
} else {
throw new AssertionError();
}
}
}
}
27 changes: 20 additions & 7 deletions src/main/java/com/stanfy/gsonxml/GsonXml.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
package com.stanfy.gsonxml;

import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.lang.reflect.Type;

import com.google.gson.Gson;
import com.google.gson.JsonIOException;
import com.google.gson.JsonSyntaxException;
Expand All @@ -14,12 +9,18 @@
import com.google.gson.stream.MalformedJsonException;
import com.stanfy.gsonxml.XmlReader.Options;

import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.lang.reflect.Type;

/**
* Wrapper for {@link Gson}.
* @author Roman Mazur (Stanfy - http://stanfy.com)
*/
public class GsonXml {

public static final String[] CHAR_ORIGINAL = new String[]{"$", "<", ">", "\"", "'"};
public static final String[] CHAR_TO = new String[]{"&amp;", "&lt;", "&gt;", "&quot;", "&apos;"};
/** Core object. */
private final Gson core;

Expand Down Expand Up @@ -95,7 +96,19 @@ private static void assertFullConsumption(final Object obj, final JsonReader rea
public <T> T fromXml(final XmlReader reader, final Type typeOfT) throws JsonIOException, JsonSyntaxException {
return core.fromJson(reader, typeOfT);
}

/**
* @param value
* @return
*/
static String decode(String value) {
if (value == null) return null;
for (int i = 0; i < CHAR_TO.length && i < CHAR_ORIGINAL.length; i++) {
String to = CHAR_TO[i];
String original = CHAR_ORIGINAL[i];
value = value.replace(to, original);
}
return value;
}
@Override
public String toString() { return core.toString(); }

Expand Down
Loading