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..."
Related
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();
Is there a way to serialize an object so that it could then be rehydrated by .Net Core Configuration Binder?
Basically, I'd like to get this Test to pass:
[Test]
public void Can_Serialize_And_Rehydrate()
{
var foo = new Foo{ Prop1 = 42; Prop2 = "Test" }
Dictionary<string, string> serialized = Serialize(Foo);
var deserializedFoo = new Foo();
new ConfigurationBuilder()
.AddInMemoryCollection(serialized)
.Build()
.Bind(deserializedFoo);
Assert.AreEqual(deserializedFoo.Prop1, 42);
Assert.AreEqual(deserializedFoo.Prop2, "Test");
}
Is there a Serializer out-of-the-box, or am I'm going to need to write my own Serialize() method?
AddInMemoryCollection's signature is like below, so why are you trying to serialize your dictionary here? You could just use it as it is.
public static IConfigurationBuilder AddInMemoryCollection(
this IConfigurationBuilder configurationBuilder,
IEnumerable<KeyValuePair<string, string>> initialData)
If you like to know more about how to test your custom configurations, I would suggest to look here:
https://github.com/aspnet/Configuration/blob/1.0.0/test/Microsoft.Extensions.Configuration.Binder.Test/ConfigurationBinderTests.cs
I was able to get this working by "hijacking" a JsonConfigurationProvider and plugging serialized Json directly into it. Not sure if this is the best way, but it does work:
public class ConfigurationSerializer
{
private class CustomJsonProvider : JsonConfigurationProvider
{
public CustomJsonProvider() : base(new JsonConfigurationSource())
{
}
public IDictionary<string, string> GetData(Stream s)
{
Load(s);
// Return the Configuration Dictionary
return Data;
}
}
public Dictionary<string, string> Serialize(object o)
{
var serialized =
JsonConvert.SerializeObject(
o,
new JsonSerializerSettings {NullValueHandling = NullValueHandling.Ignore});
using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(serialized)))
{
var jsonProvider = new CustomJsonProvider();
return jsonProvider
.GetData(ms)
.ToDictionary(key => key.Key, value => value.Value);
}
}
}
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");
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"}
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.