How to prevent Gson serialize / deserialize the first character of a field (underscore)? - json

My class:
class ExampleBean {
private String _firstField;
private String _secondField;
// respective getters and setters
}
I want to appear as follows:
{
"FirstField":"value",
"SecondField":"value"
}
And not like this
{
"_FirstField":"value",
"_SecondField":"value"
}
I initialize the parser as follows:
GsonBuilder builder = new GsonBuilder();
builder.setDateFormat(DateFormat.LONG);
builder.setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE);
builder.setPrettyPrinting();
set_defaultParser(builder.create());
I could see the API and in the documentation of "FieldNamePolicy" but I am surprised that not give the option to skip "_"
I also know I can use the annotation...
# SerializedName (" custom_naming ")
...but do not want to have to write this for alllllll my fields ...
It's very useful for me to distinguish between local variables and fields of a class. :( Any Idea?
EDIT: There would be many obvious solutions, (inheritance, gson overwriting methods, regular expresions). My question is more focused on whether there is a native solution of gson or a less intrusive fix?
Maybe we could propose as new FieldNamePolicy?

GsonBuilder provides a method setFieldNamingStrategy() that allows you to pass your own FieldNamingStrategy implementation.
Note that this replaces the call to setFieldNamingPolicy() - if you look at the source for GsonBuilder these two methods are mutually exclusive as they set the same internal field (The FieldNamingPolicy enum is a FieldNamingStrategy).
public class App
{
public static void main(String[] args)
{
Gson gson = new GsonBuilder()
.setFieldNamingStrategy(new MyFieldNamingStrategy())
.setPrettyPrinting()
.create();
System.out.println(gson.toJson(new ExampleBean()));
}
}
class ExampleBean
{
private String _firstField = "first field value";
private String _secondField = "second field value";
// respective getters and setters
}
class MyFieldNamingStrategy implements FieldNamingStrategy
{
public String translateName(Field field)
{
String fieldName =
FieldNamingPolicy.UPPER_CAMEL_CASE.translateName(field);
if (fieldName.startsWith("_"))
{
fieldName = fieldName.substring(1);
}
return fieldName;
}
}
Output:
{
"FirstField": "first field value",
"SecondField": "second field value"
}

What you want is
import java.lang.reflect.Field;
import java.text.DateFormat;
import com.google.gson.FieldNamingStrategy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
public class GsonExample {
public static void main(String... args) throws Exception {
final GsonBuilder builder = new GsonBuilder();
builder.setDateFormat(DateFormat.LONG);
builder.setPrettyPrinting();
builder.setFieldNamingStrategy(new FieldNamingStrategy() {
#Override
public String translateName(Field f) {
String fieldName = f.getName();
if(fieldName.startsWith("_") && fieldName.length() > 1) {
fieldName = fieldName.substring(1, 2).toUpperCase() + fieldName.substring(2);
}
return fieldName;
}
});
final Gson gson = builder.create();
System.out.println(gson.toJson(new ExampleBean("example", "bean")));
}
private static class ExampleBean {
private final String _firstField;
private final String _secondField;
private ExampleBean(String _firstField, String _secondField) {
this._firstField = _firstField;
this._secondField = _secondField;
}
}
}
which generates
{"FirstField":"example","SecondField":"bean"}

Related

How to solve circular reference when serializing an object which have a class member with the same type of that object

I'm facing this issue when using Gson to serialize an object which has a class member with the same type:
https://github.com/google/gson/issues/1447
The object:
public class StructId implements Serializable {
private static final long serialVersionUID = 1L;
public String Name;
public StructType Type;
public StructId ParentId;
public StructId ChildId;
And since StructId contains ParentId/ChildId with the same type I was getting infinite loop when trying to serialize it, so what I did is:
private Gson gson = new GsonBuilder()
.setExclusionStrategies(new ExclusionStrategy() {
public boolean shouldSkipClass(Class<?> clazz) {
return false; //(clazz == StructId.class);
}
/**
* Custom field exclusion goes here
*/
public boolean shouldSkipField(FieldAttributes f) {
//Ignore inner StructIds to solve circular serialization
return ( f.getName().equals("ParentId") || f.getName().equals("ChildId") );
}
})
/**
* Use serializeNulls method if you want To serialize null values
* By default, Gson does not serialize null values
*/
.serializeNulls()
.create();
But this is not good enough cause I need the data inside Parent/Child and ignoring them while serializing is not a solution.
How is it possible to solve it?
Related to the answer marked as Solution:
I have such a struct:
- Struct1
-- Table
--- Variable1
The object before serialization is:
And Json that is generated is:
As you can see, the ParentId of Table is "Struct1" but the ChildId of "Struct1" is empty and it should be "Table"
B.R.
I think using ExclusionStrategy is not the right approach to solve this problem.
I would rather suggest to use JsonSerializer and JsonDeserializer
customized for your StructId class.
(May be an approach using TypeAdapter would be even better,
but I didn't have enough Gson experience do get this working.)
So you would create your Gson instance by:
Gson gson = new GsonBuilder()
.registerTypeAdapter(StructId.class, new StructIdSerializer())
.registerTypeAdapter(StructId.class, new StructIdDeserializer())
.setPrettyPrinting()
.create();
The StructIdSerializer class below is responsible for converting a StructId to JSON.
It converts its properties Name, Type and ChildId to JSON.
Note that it does not convert the property ParentId to JSON,
because doing that would produce infinite recursion.
public class StructIdSerializer implements JsonSerializer<StructId> {
#Override
public JsonElement serialize(StructId src, Type typeOfSrc, JsonSerializationContext context) {
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("Name", src.Name);
jsonObject.add("Type", context.serialize(src.Type));
jsonObject.add("ChildId", context.serialize(src.ChildId)); // recursion!
return jsonObject;
}
}
The StructIdDeserializer class below is responsible for converting JSON to a StructId.
It converts the JSON properties Name, Type and ChildId
to corresponding Java fields in StructId.
Note that the ParentId Java field is reconstructed from the JSON nesting structure,
because it is not directly contained as a JSON property.
public class StructIdDeserializer implements JsonDeserializer<StructId> {
#Override
public StructId deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
StructId id = new StructId();
id.Name = json.getAsJsonObject().get("Name").getAsString();
id.Type = context.deserialize(json.getAsJsonObject().get("Type"), StructType.class);
JsonElement childJson = json.getAsJsonObject().get("ChildId");
if (childJson != null) {
id.ChildId = context.deserialize(childJson, StructId.class); // recursion!
id.ChildId.ParentId = id;
}
return id;
}
}
I tested the code above with this JSON input example
{
"Name": "John",
"Type": "A",
"ChildId": {
"Name": "Jane",
"Type": "B",
"ChildId": {
"Name": "Joe",
"Type": "A"
}
}
}
by deserializing it with
StructId root = gson.fromJson(new FileReader("example.json"), StructId.class);,
then by serializing that with
System.out.println(gson.toJson(root));
and got the original JSON again.
Just to show one way to make serialization (so I do not handle de-serialization) with TypeAdapter and ExclusionStrategy. This might not be the most beautiful implementation but it is quite generic anyway.
This solution makes use of the fact that your struct is some kind of a bi-directional linked list and given any node in that list we just need to separate the serialization of parents and children so that those are serialized just in one direction to avoid circular references.
First we need configurable ExclusionStrategy like:
public class FieldExclusionStrategy implements ExclusionStrategy {
private final List<String> skipFields;
public FieldExclusionStrategy(String... fieldNames) {
skipFields = Arrays.asList(fieldNames);
}
#Override
public boolean shouldSkipField(FieldAttributes f) {
return skipFields.contains(f.getName());
}
#Override
public boolean shouldSkipClass(Class<?> clazz) {
return false;
}
}
Then the TypeAdapter would be like:
public class LinkedListAdapter extends TypeAdapter<StructId> {
private static final String PARENT_ID = "ParentId";
private static final String CHILD_ID = "ChildId";
private Gson gson;
#Override
public void write(JsonWriter out, StructId value) throws IOException {
// First serialize everything but StructIds
// You could also use type based exclusion strategy
// but for brevity I use just this one
gson = new GsonBuilder()
.addSerializationExclusionStrategy(
new FieldExclusionStrategy(CHILD_ID, PARENT_ID))
.create();
JsonObject structObject = gson.toJsonTree(value).getAsJsonObject();
JsonObject structParentObject;
JsonObject structChildObject;
// If exists go through the ParentId side in one direction.
if(null!=value.ParentId) {
gson = new GsonBuilder()
.addSerializationExclusionStrategy(new FieldExclusionStrategy(CHILD_ID))
.create();
structObject.add(PARENT_ID, gson.toJsonTree(value.ParentId));
if(null!=value.ParentId.ChildId) {
gson = new GsonBuilder()
.addSerializationExclusionStrategy(new FieldExclusionStrategy(PARENT_ID))
.create();
structParentObject = structObject.get(PARENT_ID).getAsJsonObject();
structParentObject.add(CHILD_ID, gson.toJsonTree(value.ParentId.ChildId).getAsJsonObject());
}
}
// And also if exists go through the ChildId side in one direction.
if(null!=value.ChildId) {
gson = new GsonBuilder()
.addSerializationExclusionStrategy(new FieldExclusionStrategy(PARENT_ID))
.create();
structObject.add(CHILD_ID, gson.toJsonTree(value.ChildId));
if(null!=value.ChildId.ParentId) {
gson = new GsonBuilder()
.addSerializationExclusionStrategy(new FieldExclusionStrategy(CHILD_ID))
.create();
structChildObject = structObject.get(CHILD_ID).getAsJsonObject();
structChildObject.add(PARENT_ID, gson.toJsonTree(value.ChildId.ParentId).getAsJsonObject());
}
}
// Finally write the combined result out. No need to initialize gson anymore
// since just writing JsonElement
gson.toJson(structObject, out);
}
#Override
public StructId read(JsonReader in) throws IOException {
return null;
}}
Testing it:
#Slf4j
public class TestIt extends BaseGsonTest {
#Test
public void test1() {
StructId grandParent = new StructId();
StructId parent = new StructId();
grandParent.ChildId = parent;
parent.ParentId = grandParent;
StructId child = new StructId();
parent.ChildId = child;
child.ParentId = parent;
Gson gson = new GsonBuilder()
.setPrettyPrinting()
.registerTypeAdapter(StructId.class, new LinkedListAdapter())
.create();
log.info("\n{}", gson.toJson(parent));
}}
Would give you something like:
{
"Name": "name1237598030",
"Type": {
"name": "name688766789"
},
"ParentId": {
"Name": "name1169146729",
"Type": {
"name": "name2040352617"
}
},
"ChildId": {
"Name": "name302155142",
"Type": {
"name": "name24606376"
}
}
}
Names in my test material are just by default initialized with "name"+hashCode()
Sorry for misleading you guys, based on this post :
Is there a solution about Gson "circular reference"?
"there is no automated solution for circular references in Gson. The only JSON-producing library I know of that handles circular references automatically is XStream (with Jettison backend)."
But that is case you don't use Jackson! If you are using Jackson already for building your REST API controllers so why not to use it for making the serialization. No need for external compopnents like: Gson or XStream.
The solution with Jackson:
Serialization:
ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
try {
jsonDesttinationIdString = ow.writeValueAsString(destinationId);
} catch (JsonProcessingException ex) {
throw new SpecificationException(ex.getMessage());
}
De-serialization:
ObjectMapper mapper = new ObjectMapper();
try {
destinationStructId = destinationId.isEmpty() ? null : mapper.readValue(URLDecoder.decode(destinationId, ENCODING), StructId.class);
} catch (IOException e) {
throw new SpecificationException(e.getMessage());
}
And most important, you must use the #JsonIdentityInfo annotation:
//#JsonIdentityInfo(
// generator = ObjectIdGenerators.PropertyGenerator.class,
// property = "Name")
#JsonIdentityInfo(
generator = ObjectIdGenerators.UUIDGenerator.class,
property = "id")
public class StructId implements Serializable {
private static final long serialVersionUID = 1L;
#JsonProperty("id") // I added this field to have a unique identfier
private UUID id = UUID.randomUUID();

gson flat json to nested objects needs serializer/deserializer?

I have some JSON coming in (I don't have any control or ability to change the structure and/or naming within the JSON...important to keep in mind in this question) that has a "flat" structure similar to this:
{
"name": "...",
"email": "...",
"box_background_color": "...",
"box_border_color": "...",
"box_text_color": "...",
...
}
Now, I can just create a simple object that keeps everything flat, like so:
public class Settings {
#SerializedName("name")
private String _name;
#SerializedName("email")
private String _emailAddress;
#SerializedName("box_background_color")
private String _boxBackgroundColor;
#SerializedName("box_border_color")
private String _boxBorderColor;
#SerializedName("box_text_color")
private String _boxTextColor;
...
}
However, I want everything associated with box settings to be in it's own class (BoxSettings). This is more like what I want:
public class Settings {
#SerializedName("name")
private String _name;
#SerializedName("email")
private String _emailAddress;
private BoxSettings _boxSettings
...
}
public class BoxSettings {
#SerializedName("box_background_color")
private String _boxBackgroundColor;
#SerializedName("box_border_color")
private String _boxBorderColor;
#SerializedName("box_text_color")
private String _boxTextColor;
...
}
I know that if the JSON was structured such that the box settings were nested then it would be easy to accomplish what I want, however, I don't have the ability to change the structure of the JSON, so please don't suggest that (I would do it if I could).
My question is this: Is creating an entire TypeAdapter the only way to accomplish what I want or can I still accomplish most of this with annotations? If it is not the only way, how else can I accomplish this without changing the JSON at all?
The following is an example of what I mean by "creating an entire TypeAdapter":
public class SettingsTypeAdapter implements JsonDeserializer<Settings>, JsonSerializer<Settings> {
#Override
public JsonElement serialize(Settings src, Type typeOfSrc, JsonSerializationContext context) {
// Add _name
// Add _emailAddress
// Add BoxSettings._boxBackgroundColor
// Add BoxSettings._boxBorderColor
// Add BoxSettings._boxTextColor
return jsonElement;
}
#Override
public Settings deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
// Read _name
// Read _emailAddress
// Read BoxSettings._boxBackgroundColor
// Read BoxSettings._boxBorderColor
// Read BoxSettings._boxTextColor
return settings;
}
}
The TypeAdapter is not the only way, but in this case would be the best way since you can associate the adapter with a Gson instance (or whatever library you are using) and have all your mapping code there.
Another way is to use JAVA reflection. I've used a version of the below code in my projects before but never with JSON and never with nested objects (mostly when there was no other choice or if i wanted to map a SQL result set to a Java object without calling resultSet.get... a lot of times).
This will work in this case.
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.json.JSONObject;
public class Main {
public static void main(String[] args) {
try {
String json = "{\"name\": \"test name\", \"email\": \"email#email.com\", \"box_background_color\": \"red\", \"box_border_color\": \"orange\", \"box_text_color\": \"white\", \"test3_var2\":3}";
JSONObject jsonObject = new JSONObject(json);
System.out.println(jsonObject);
System.out.println();
/*
* need to parse JSON into a map of String, Object
*/
Map<String, Object> mapAll = new HashMap<String, Object>();
Iterator<String> iter = jsonObject.keys();
while (iter.hasNext()) {
String key = (String) iter.next();
Object value = jsonObject.get(key);
mapAll.put(key, value);
System.out.println(key + "::::" + value);
}
System.out.println();
/*
* use the mapper to generate the objects
*/
MyMapper<TestClass1> myMapper = new MyMapper<TestClass1>();
TestClass1 result = myMapper.mapToObject(mapAll, TestClass1.class);
System.out.println(result);
} catch (Exception e) {
e.printStackTrace();
}
}
}
class MyMapper<T> {
#SuppressWarnings("unchecked")
public T mapToObject(Map<String, Object> flatStructure, Class<T> objectClass) {
T result = null;
Field[] fields = null;
try {
// new base object
result = objectClass.newInstance();
// get all of its fields
fields = objectClass.getDeclaredFields();
for (Field field : fields) {
// normal variable
if (field.isAnnotationPresent(MyColumn.class)) {
String variableKey = field.getAnnotation(MyColumn.class).variableKey();
setJavaFieldValue(result, field.getName(), flatStructure.get(variableKey));
}
// variable that is an object and itself has to be mapped
else if (field.isAnnotationPresent(MyInnerColumn.class)) {
String startsWith = field.getAnnotation(MyInnerColumn.class).startsWith();
// reduce the map to only have attributes that are related to this field
Map<String, Object> reducedMap = reduceMap(startsWith, flatStructure);
// make sure that there are attributes for the inner object
if (reducedMap != null) {
// map the inner object
MyMapper<T> myMapper = new MyMapper<T>();
T t2 = myMapper.mapToObject(reducedMap, (Class<T>) field.getType());
// set the mapped object to the base objecct
setJavaFieldValue(result, field.getName(), t2);
}
} else {
// no annotation on the field so ignored
}
}
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
private Map<String, Object> reduceMap(String startsWith, Map<String, Object> mapToReduce) {
Map<String, Object> result = new HashMap<String, Object>();
for (Map.Entry<String, Object> entry : mapToReduce.entrySet()) {
if (entry.getKey().toLowerCase().startsWith(startsWith.toLowerCase())) {
result.put(entry.getKey(), entry.getValue());
}
}
return result.size() == 0 ? null : result;
}
private void setJavaFieldValue(Object object, String fieldName, Object fieldValue) {
try {
Field field = object.getClass().getDeclaredField(fieldName);
boolean fieldAccess = field.isAccessible();
// make the field accessible
field.setAccessible(true);
field.set(object, fieldValue);
// put it back to the way it was
field.setAccessible(fieldAccess);
} catch (Exception e) {
e.printStackTrace();
}
}
}
/*
* Annotation for a regular variable / field
*/
#Target(ElementType.FIELD)
#Retention(RetentionPolicy.RUNTIME)
#interface MyColumn {
// the variable's JSON key
String variableKey() default "";
}
/*
* Annotation for an inner / nested variable / field
*/
#Target(ElementType.FIELD)
#Retention(RetentionPolicy.RUNTIME)
#interface MyInnerColumn {
/*
* JSON keys that start with this string will be
* associated with this nested field
*/
String startsWith() default "";
}
class TestClass1 {
#MyColumn(variableKey = "name")
private String _name;
#MyColumn(variableKey = "email")
private String _emailAddress;
#MyInnerColumn(startsWith = "box_")
private TestClass2 innerClass;
#MyInnerColumn(startsWith = "test3_")
private TestClass3 innerClass2;
#Override
public String toString() {
return "TestClass1 [_name=" + _name + ", _emailAddress=" + _emailAddress + ", innerClass=" + innerClass + ", innerClass2=" + innerClass2 + "]";
}
}
class TestClass2 {
#MyColumn(variableKey = "box_background_color")
private String _boxBackgroundColor;
#MyColumn(variableKey = "box_border_color")
private String _boxBorderColor;
#MyColumn(variableKey = "box_text_color")
private String _boxTextColor;
#Override
public String toString() {
return "TestClass2 [_boxBackgroundColor=" + _boxBackgroundColor + ", _boxBorderColor=" + _boxBorderColor
+ ", _boxTextColor=" + _boxTextColor + "]";
}
}
class TestClass3 {
#MyColumn(variableKey = "test3_var1")
private String _test3Var1;
#MyColumn(variableKey = "test3_var2")
private int _test3Var2;
#Override
public String toString() {
return "TestClass3 [_test3Var1=" + _test3Var1 + ", _test3Var2=" + _test3Var2 + "]";
}
}
Output
{"box_background_color":"red","box_text_color":"white","test3_var2":3,"name":"test name","email":"email#email.com","box_border_color":"orange"}
box_background_color::::red
box_text_color::::white
test3_var2::::3
name::::test name
email::::email#email.com
box_border_color::::orange
TestClass1 [_name=test name, _emailAddress=email#email.com, innerClass=TestClass2 [_boxBackgroundColor=red, _boxBorderColor=orange, _boxTextColor=white], innerClass2=TestClass3 [_test3Var1=null, _test3Var2=3]]

Remove namespace prefix while JAXB marshalling

I have JAXB objects created from a schema. While marshalling, the xml elements are getting annotated with ns2. I have tried all the options that exist over the net for this problem, but none of them works. I cannot modify my schema or change package-info.java. Please help
After much research and tinkering I have finally managed to achieve a solution to this problem. Please accept my apologies for not posting links to the original references - there are many and I wasn't taking notes - but this one was certainly useful.
My solution uses a filtering XMLStreamWriter which applies an empty namespace context.
public class NoNamesWriter extends DelegatingXMLStreamWriter {
private static final NamespaceContext emptyNamespaceContext = new NamespaceContext() {
#Override
public String getNamespaceURI(String prefix) {
return "";
}
#Override
public String getPrefix(String namespaceURI) {
return "";
}
#Override
public Iterator getPrefixes(String namespaceURI) {
return null;
}
};
public static XMLStreamWriter filter(Writer writer) throws XMLStreamException {
return new NoNamesWriter(XMLOutputFactory.newInstance().createXMLStreamWriter(writer));
}
public NoNamesWriter(XMLStreamWriter writer) {
super(writer);
}
#Override
public NamespaceContext getNamespaceContext() {
return emptyNamespaceContext;
}
}
You can find a DelegatingXMLStreamWriter here.
You can then filter the marshalling xml with:
// Filter the output to remove namespaces.
m.marshal(it, NoNamesWriter.filter(writer));
I am sure there are more efficient mechanisms but I know this one works.
For me, only changing the package-info.java class worked like a charm, exactly as zatziky stated :
package-info.java
#javax.xml.bind.annotation.XmlSchema
(namespace = "http://example.com",
xmlns = {#XmlNs(prefix = "", namespaceURI = "http://example.com")},
elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
package my.package;
import javax.xml.bind.annotation.XmlNs;
You can let the namespaces be written only once. You will need a proxy class of the XMLStreamWriter and a package-info.java. Then you will do in your code:
StringWriter stringWriter = new StringWriter();
XMLStreamWriter writer = new Wrapper((XMLStreamWriter) XMLOutputFactory
.newInstance().createXMLStreamWriter(stringWriter));
JAXBContext jaxbContext = JAXBContext.newInstance(Collection.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
jaxbMarshaller.marshal(books, writer);
System.out.println(stringWriter.toString());
Proxy class (the important method is "writeNamespace"):
class WrapperXMLStreamWriter implements XMLStreamWriter {
private final XMLStreamWriter writer;
public WrapperXMLStreamWriter(XMLStreamWriter writer) {
this.writer = writer;
}
//keeps track of what namespaces were used so that not to
//write them more than once
private List<String> namespaces = new ArrayList<String>();
public void init(){
namespaces.clear();
}
public void writeStartElement(String localName) throws XMLStreamException {
init();
writer.writeStartElement(localName);
}
public void writeStartElement(String namespaceURI, String localName) throws XMLStreamException {
init();
writer.writeStartElement(namespaceURI, localName);
}
public void writeStartElement(String prefix, String localName, String namespaceURI) throws XMLStreamException {
init();
writer.writeStartElement(prefix, localName, namespaceURI);
}
public void writeNamespace(String prefix, String namespaceURI) throws XMLStreamException {
if(namespaces.contains(namespaceURI)){
return;
}
namespaces.add(namespaceURI);
writer.writeNamespace(prefix, namespaceURI);
}
// .. other delegation method, always the same pattern: writer.method() ...
}
package-info.java:
#XmlSchema(elementFormDefault=XmlNsForm.QUALIFIED, attributeFormDefault=XmlNsForm.UNQUALIFIED ,
xmlns = {
#XmlNs(namespaceURI = "http://www.w3.org/2001/XMLSchema-instance", prefix = "xsi")})
package your.package;
import javax.xml.bind.annotation.XmlNs;
import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;
You can use the NamespacePrefixMapper extension to control the namespace prefixes for your use case. The same extension is supported by both the JAXB reference implementation and EclipseLink JAXB (MOXy).
http://wiki.eclipse.org/EclipseLink/Release/2.4.0/JAXB_RI_Extensions/Namespace_Prefix_Mapper
Every solution requires complex overwriting or annotations which seems not to work with recent version. I use a simpler approach, just by replacing the annoying namespaces. I wish Google & Co would use JSON and get rid of XML.
kml.marshal(file);
String kmlContent = FileUtils.readFileToString(file, "UTF-8");
kmlContent = kmlContent.replaceAll("ns2:","").replace("<kml xmlns:ns2=\"http://www.opengis.net/kml/2.2\" xmlns:ns3=\"http://www.w3.org/2005/Atom\" xmlns:ns4=\"urn:oasis:names:tc:ciq:xsdschema:xAL:2.0\" xmlns:ns5=\"http://www.google.com/kml/ext/2.2\">", "<kml>");
FileUtils.write(file, kmlContent, "UTF-8");

GSON not handlig initialized static list correctly

If I do this:
public static volatile ArrayList<Process> processes = new ArrayList<Process>(){
{
add(new Process("News Workflow", "This is the workflow for the news segment", "image"));
}
};
and then this:
String jsonResponse = gson.toJson(processes);
jsonResponse is null.
But if I do this:
public static volatile ArrayList<Process> processes = new ArrayList<Process>();
processes.add(new Process("nam", "description", "image"));
String jsonResponse = gson.toJson(processes);
Json response is:
[{"name":"nam","description":"description","image":"image"}]
Why is that?
I do not know what is the problem with Gson, but do you know, that you are creating subclass of ArrayList here?
new ArrayList<Process>(){
{
add(new Process("News Workflow", "This is the workflow for the news segment", "image"));
}
};
You can check that by
System.out.println( processes.getClass().getName() );
it won't print java.util.ArrayList.
I think you wanted to use static initialization as
public static volatile ArrayList<Process> processes = new ArrayList<Process>();
static {
processes.add( new Process( "News Workflow", "This is the workflow for the news segment", "image" ) );
};
It seems that there is problem with anonymous classes, same problem is here
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
public class GSonAnonymTest {
interface Holder {
String get();
}
static Holder h = new Holder() {
String s = "value";
#Override
public String get() {
return s;
}
};
public static void main( final String[] args ) {
final GsonBuilder gb = new GsonBuilder();
final Gson gson = gb.create();
System.out.println( "h:" + gson.toJson( h ) );
System.out.println( h.get() );
}
}
UPD: look at Gson User Guide - Finer Points with Objects, last point "...anonymous classes, and local classes are ignored and not included in serialization or deserialization..."

How to serialize such a custom type to json with google-gson?

First, I have a very simple java bean which can be easily serialized to json:
class Node {
private String text;
// getter and setter
}
Node node = new Node();
node.setText("Hello");
String json = new Gson().toJson(node);
// json is { text: "Hello" }
Then in order to make such beans have some dynamic values, so I create a "WithData" base class:
Class WithData {
private Map<String, Object> map = new HashMap<String, Object>();
public void setData(String key, Object value) { map.put(key, value); }
public Object getData(String key) = { return map.get(key); }
}
class Node extends WithData {
private String text;
// getter and setter
}
Now I can set more data to a node:
Node node = new Node();
node.setText("Hello");
node.setData("to", "The world");
But Gson will ignore the "to", the result is still { text: "Hello" }. I expect it to be: { text: "Hello", to: "The world" }
Is there any way to write a serializer for type WithData, that all classes extend it will not only generate its own properties to json, but also the data in the map?
I tried to implement a custom serializer, but failed, because I don't know how to let Gson serialize the properties first, then the data in map.
What I do now is creating a custom serializer:
public static class NodeSerializer implements JsonSerializer<Node> {
public JsonElement serialize(Node src,
Type typeOfSrc, JsonSerializationContext context) {
JsonObject obj = new JsonObject();
obj.addProperty("id", src.id);
obj.addProperty("text", src.text);
obj.addProperty("leaf", src.leaf);
obj.addProperty("level", src.level);
obj.addProperty("parentId", src.parentId);
obj.addProperty("order", src.order);
Set<String> keys = src.getDataKeys();
if (keys != null) {
for (String key : keys) {
obj.add(key, context.serialize(src.getData(key)));
}
}
return obj;
};
}
Then use GsonBuilder to convert it:
Gson gson = new GsonBuilder().
registerTypeAdapter(Node.class, new NodeSerializer()).create();
Tree tree = new Tree();
tree.addNode(node1);
tree.addNode(node2);
gson.toJson(tree);
Then the nodes in the tree will be converted as I expected. The only boring thing is that I need to create a special Gson each time.
Actually, you should expect Node:WithData to serialize as
{
"text": "Hello",
"map": {
"to": "the world"
}
}
(that's with "pretty print" turned on)
I was able to get that serialization when I tried your example. Here is my exact code
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.net.MalformedURLException;
import java.util.HashMap;
import java.util.Map;
public class Class1 {
public static void main(String[] args) throws MalformedURLException {
GsonBuilder gb = new GsonBuilder();
Gson g = gb.setPrettyPrinting().create();
Node n = new Node();
n.setText("Hello");
n.setData("to", "the world");
System.out.println(g.toJson(n));
}
private static class WithData {
private Map<String, Object> map = new HashMap<String, Object>();
public void setData(String key, Object value) { map.put(key, value); }
public Object getData(String key) { return map.get(key); }
}
private static class Node extends WithData {
private String text;
public Node() { }
public String getText() {return text;}
public void setText(String text) {this.text = text;}
}
}
I was using the JDK (javac) to compile - that is important because other compilers (those included with some IDEs) may remove the information on which Gson relies as part of their optimization or obfuscation process.
Here are the compilation and execution commands I used:
"C:\Program Files\Java\jdk1.6.0_24\bin\javac.exe" -classpath gson-2.0.jar Class1.java
"C:\Program Files\Java\jdk1.6.0_24\bin\java.exe" -classpath .;gson-2.0.jar Class1
For the purposes of this test, I put the Gson jar file in the same folder as the test class file.
Note that I'm using Gson 2.0; 1.x may behave differently.
Your JDK may be installed in a different location than mine, so if you use those commands, be sure to adjust the path to your JDK as appropriate.