I would like to convert an object into a string representation which is like JSON without the fieldnames
for instance the following class
public class Employee{
private String name;
private int age;
private boolean married;
}
Employee = new objEmp();
objEmp.Name = "Mickey Mouse";
objEmp.age = 24;
objEmp.married = false;
the above object i would like to convert into string as
{"Mickey Mouse", 24, false}
and the array of this object must look like
[
{"Mickey Mouse", 24, false}
,{"Robin Hood" , 24, false}
]
I am looking for a solution that can be applied constantly for every java Objects (POJO).
so overriding toString() method of each object or solutions on the similar lines are not desired
I prefer it to do with jackson as i am all ready using it to convert Objects into JSON in Spring 3
I am looking for some jackson ObjectMapper configurations that can help me to achieve this
or if not i can create my own Object Mapper and use it in Spring View
thanks
I don't think ObjectMapper has the flexibility to do this, since what you are trying to generate is not actually JSON.
How about using reflection to get a list of the field values, and then serializing this as an Object array?
Something like:
List<Object> vals = new ArrayList<>();
for (Field field : Employee.class.getDeclaredFields()) {
field.setAccessible(true);
vals.add(field.get(emp));
}
ObjectMapper om = new ObjectMapper();
System.out.println(om.writeValueAsString(vals));
This gives you
["Mickey Mouse",24,false]
which has square brackets instead of curly braces. Is this good enough?
Related
I know we can use Jackson MixIn's to rename a property or to ignore a property (see examples here). But is it possible to add a property?
The added property can be:
A constant (such as a version number)
A computed value (e.g. if the source class has properties for getWidth() and getHeight(), but we want to ignore both and export a getArea() instead)
Flattened information from nested members (e.g. a class has a member Information which in turn has a member Description, and we want to have a new property for description and skipping the nesting structure of Information)
From documentation:
"Mix-in" annotations are a way to associate annotations with classes,
without modifying (target) classes themselves, originally intended to
help support 3rd party datatypes where user can not modify sources to
add annotations.
With mix-ins you can:
1. Define that annotations of a '''mix-in class''' (or interface)
2. will be used with a '''target class''' (or interface) such that it
appears
3. as if the ''target class'' had all annotations that the ''mix-in''
class has (for purposes of configuring serialization /
deserialization)
To solve your problems you can:
Create new POJO which has all required fields.
Implement custom serialiser.
Before serialisation convert POJO to Map and add/remove nodes.
Use com.fasterxml.jackson.databind.ser.BeanSerializerModifier to extend custom serialisers. See: Jackson custom serialization and deserialization.
For example, to add a constant version to each object you can wrap it in Verisoned class:
class Versioned {
private final String version;
#JsonUnwrapped
private final Object pojo;
public Versioned(String version, Object pojo) {
this.version = version;
this.pojo = pojo;
}
public String getVersion() {
return version;
}
public Object getPojo() {
return pojo;
}
}
Now, if you wrap an Arae(width, height) object:
Area area = new Area(11, 12);
String json = mapper.writeValueAsString(new Versioned("1.1", area));
output will be:
{
"version" : "1.1",
"width" : 11,
"height" : 12
}
I am having a problem using Morphia with a custom Mapper/Type Converter
Given following POJO
#Entity("users")
public class User{
private String username = null;
private String password = null;
}
problem is, in the given MongoDB (not under my control) the values are not simply laid out like
{
"email": "xy#test.com",
"password": "abc"
}
but the objects look more like
{
"usersettings": {
"email": "xy#test.com",
"password": [
"abc", "cde", "efg"
]
}
}
(The real world Mongo document is much more complex as you may expect)
So I have to map "usersettings.email" to my "username" member and "usersettings.password.0" (first array only) to my "password" member.
I know there are TypeConverters in Morphia and you can register them, but they only work for members, not for classes.
In other words this is not working (it is just ignored at runtime):
#Entity("users")
#Converters("MyUserConverter.class") <-- this does NOT WORK!
public class User{
private String email = null;
private String password = null;
}
It would work for members like this:
#Entity("users")
public class User{
private String email = null;
#Converters("MyCustomTypeConverter.class") <-- this would work, but does not help in my case!
private MyCustomType password = null;
}
Problem is, I need to map the whole class and not only certain members.
How can I do that?
Morphia doesn't support something quite like that. The document structure generally has to match the object structure. However, you can use the lifecycle annotations to massage the DBObject used to load and save your entities. In this you could use #PreLoad to reshape the DBObject coming in to reflect the locations expected by your Java objects. And then #PrePersist to move those values back under usersettings before writing back to mongo.
I'm trying to deserialize two types of json:
{
name: "bob",
worksAt: {
name: "Bobs department store",
location: "downtown"
},
age: 46
}
and
{
name: "Tom",
worksAt: "company:Bobs department store",
age: 27
}
into these objects:
The first way creates two new objects, the second way requests the object from the database based on the contents of a string.
sort of like how jackson mapper can deserialize an arbitrary string into an object, for objects like this:
public class Company{
public String name;
public Employee[] employees
public Company(){}
public Company(String json){
//turn string into object using whatever encoding you want blah blah blah...
}
}
The trouble is I need both. I need it to handle objects and strings. Both could arrive from the same input.
The first think I tried was making a Converter
It says these create a delegate type to pass to the deserializer, but the converter is always applied even when the datatype isn't a string. So that didn't work.
I've also tried a normal deserializer, but I can't find a way to defer to the BeanDeserializer. The beanDeserializer is so complicated that I can't manually instantiate it. I also see no way to defer to a default deserializer in jackson mapper.
Do I have to re-implement jackson mappers deserialization to do this? Is there any way for a deserializer to say "I can't do this, use the default implementation."?
Edit: Some further progress. Based on the Jackson Mapper source code, it looks like you can instatiate bean deserializers like this:
DeserializationConfig config = ctxt.getConfig();
JavaType type = config.constructType(_valueClass);
BeanDescription introspect = config.introspect(type);
JsonDeserializer<Object> beanDeserializer = ctxt.getFactory().createBeanDeserializer(ctxt, type , introspect);
but for some reason all the _beanProperties have the FailingDeserializer set for their _valueDeserializer and the whole thing fails. So I have no idea why that happens...
Have you tried writing a custom deserializer? This gives you the most control on how Jackson deserializes the object. You may be able to try to deserialize one way, and if there's an error, try another way.
Jackson can also handle polymorphic deserialization, though this would require a small change to the json to include type information, and it sounds like your problem constraints might not allow that.
If I understand the problem correctly, I would recommend using JsonNode. You can define a setter in your top-level type like this:
setWorksAt(JsonNode node) {
if (node.getNodeType == JsonNodeType.STRING) {
String name = node.getText();
name = name.substring(name.lastIndexOf(':'));
this.company = new Company(name);
} else if (node.getNodeType == JsonNodeType.OBJECT) {
this.company = mapper.treeToValue(node, Company.class);
}
}
That allows you to handle the two separate worksFor inputs, while still allowing the standard mapper to handle any substructures for the OBJECT case.
With recent versions of Jackson (2.8+ I think, definitely works with 2.9) you can use multiple #JsonCreator and do something like this:
public class Company {
private String name;
private String location;
private Company(String name, String location) {
this.name = name;
this.location = location;
}
private Company(String stringRepresentation) {
// add code here to parse string and extract name and location
}
#JsonCreator
private static Company fromJson(
#JsonProperty("name") String name,
#JsonProperty("location") String location)
{
return new Company(name, location);
}
#JsonCreator
private static Company fromJson(String str) {
return Company(str);
}
}
My JsonFX serialization code works, but the object that I'm serializing contains a list of polymorphic entities, and they're all deserialized as their base type and not their actual type.
Here's my serialization code:
public static string Serialize(System.Object obj)
{
StringBuilder builder = new StringBuilder();
using (TextWriter textWriter = new StringWriter(builder))
{
JsonWriter writer = new JsonWriter(textWriter);
writer.Write(obj);
return builder.ToString();
}
}
public static T Deserialize<T>(string json)
{
using (TextReader textReader = new StringReader(json))
{
var jsonReader = new JsonReader(textReader);
return jsonReader.Deserialize<T>();
}
}
As you can see it's pretty straightforward. I'm also not decorating my classes with any attributes or anything special to make them serializable. Besides the polymorphic problem, it all just seems to be working properly.
So how can I get my polymorphic types to deserialize properly?.
Thanks.
You need to turn on type hinting. Here's example code (this is relevant to JsonFx v1.4, may or may not work with your version):
StringBuilder result = new StringBuilder(string.Empty);
JsonWriterSettings settings = JsonDataWriter.CreateSettings(true);
settings.TypeHintName = "__type";
JsonWriter writer = new JsonWriter(result, settings);
writer.Write(obj);
return result.ToString();
This will add extra data to your JSON string which looks something like:
"__type": "MyNamespace.MyClass, MyAssembly",
Which means it finds out what derived type it is based on the class name. If you change your class name or namespace name, it won't work anymore. Worse, you can't deserialize from your old JSON text data anymore, unless you mass replace all occurrences of the old class name and replace it with the new.
So you have to be careful with it.
EDIT: Forgot to mention that you have to edit the source code of JsonFx for this to work.
In JsonReader.cs, find the ReadArray method:
Change:
object value = this.Read(arrayItemType, isArrayTypeAHint);
to:
object value = this.Read(null, false);
This will ensure that JsonFx will always attempt to figure out the type of each element in an array/list. If you want this to work for just single variables, well you'd need to do the changes on the appropriate code (haven't tried that).
I was wondering if there is an easy way to convert the javafx.util.Properties object to a java.util.HashMap.
There is the obvious way of getting each value from the Properties object and putting it in a Map. But with a large number of properties it seems like there should be a way of just getting the Map that backs javafx.util.Properties (if it is a Map).
Thanks in advance for any suggestions.
I don't really know if javafx.util.Properties are backed by Java Map, but since public API does not mention any way to get this map you probably shouldn't try to do it - even if it was possible (e.g. by extending Properties class) it might change in future versions.
I would stay with copying every element.
+1 for pazabos answer. But I would go the other way around: extend HashMap or java.util.Properties which then could export javafx.util.Properties (or hold an instance) sth. like:
class MyProperties extends HashMap {
HashSet<String> keys = new HashSet<String>();
javafx.util.Properties p = new Properties();
public String get(String str) {
return p.get(str);
}
public Map creatHashMap() {
Map map = new HashMap();
for (String k : keys) {
map.put(k, p.get(k));
}
return map;
}
public void put() {
//...
}