I've searched for a way to to convert a POJO into a BSON document. Unfortunately, the only answer you can find on the web is that MongoDB does this implicitly through the db.getCollection("some collection", Pojo.class) function. But there are some use cases where you want to convert a POJO into a BSON document.
One could be that you want to update a nested document in your MongoDB root document.
However, this isn't possible with the provided methods of the MongoDB Java Driver. The only way to do this is to write all the values manually in to a update expression.
db.getCollection("some collection", Pojo.class)
.updateOne(Filter.eq(id), Updates.set("myNestedDoc", new Document("some", "value"));
But it would be nice to do the same with a POJO. The advantages are obvious! Less duplicated code, better maintainable and less error prune.
But how can we accomplish this?
As described above, no answer can be found on the web. So I've searched the MongoDB Java Driver source code and found a way!
The BsonDocumentWrapper is a utility class to wrap a object as a BsonDocument. It uses the CodecRegistry which does the conversion internally.
db.getCollection("some collection", Pojo.class)
.updateOne(Filter.eq(id),
Updates.set("myNestedDoc",
BsonDocumentWrapper.asBsonDocument(lom, codecRegistry)));
)
You could also use the CodecRegistry object directly.
Pojo myPojo...
BsonDocument unwrapped = new BsonDocument();
BsonWriter writer = new BsonDocumentWriter(unwrapped);
Encoder<Pojo> encoder = codecRegistry.get(Pojo.class);
encoder.encode(writer, myPojo, EncoderContext.builder().build());
this.unwrapped = unwrapped;
But the utility class is more readable and maintainable.
This way you can safely use your existing POJOs to update your nested structures.
Keep in mind that you will replace the whole sub object, not only the fields that have been changed!
If you for some reason need to convert a BSON into a POJO you can use this sniped:
BsonDocument bson...
BsonReader reader = new BsonDocumentReader(bson);
Decoder<Pojo> encoder = codecRegistry.get(Pojo.class);
Pojo myPojo = encoder.decode(reader, DecoderContext.builder().build());
I hope this is useful to someone ;)
Related
I have a bunch of Document in a Collection and would like to retrieve all of them. This is my situation:
I am using the Java Reactive Streams driver
I am using the CodecRegistry to get my Document deserialized to my Pojo
The problem is that all flavours of the find() method returns a FindPublisher<Pojo> and needlessly to say that any kind of value emission will result in the returning of Pojo object. I want a List<Pojo> or a Set<Pojo> returned. How do I return a List<Pojo or a Set<Pojo>?
In the quickstart, they are using the find().first() which returns a single Document and hence a single Pojo object makes sense. There is no example for returning multiple Document.
Using MongoDB Reactive Streams Driver and RxJava, for example:
Publisher<Document> publisher = collection.find();
List<Document> docs = Flowable.fromPublisher(publisher)
.blockingStream()
.collect(Collectors.toList());
[EDIT ADD]
You can use a non-blocking call, for example:
List<Document> docs = new ArrayList<>();
Observable.fromPublisher(publisher).subscribe(doc -> docs.add(doc));
The few times I've worked with Java/Rest/JSon, JSON elements have always been built in camelCase format.
For example:
"someField": {
"someSonField1": "20191106",
"someSonField2": "20201119",
...
}
However, in a functional document they have passed me to build a Rest JSon client, they use this notation:
"some_field": {
"some_son_field_1": "20191106",
"some_son_field_2": "20201119",
...
}
Is it expressed somewhere that Java has to use the notation 1?.
It seems to me that if it is done this way, everything goes much more smoothly when modeling the objects:
#XmlRootElement(name = "someField")
#XmlType(propOrder = {"someSonField1", "someSonField2"})
public class someField {
private String someSonField1;
private String someSonField2;
//...
}
Thanks!
Q: Is it expressed somewhere that Java has to use the notation?
A: No: it's 100% "convention", not mandatory.
As it happens, the standard convention for both JSON (a creature of Javascript) and Java is camelcase. For example: Java Naming Conventions.
some_son_field_1 is an example of snake case. It's associated with classic "C" programs. It's also common (but NOT universal) with XML. It, too, is a "convention" - not a requirement.
I'm curious why you're choosing XML bindings for JSON data. Have you considered using Jackson?
Finally, this article might be of interest to you:
5 Basic REST API Design Guidelines
I see you're using javax.xml.bind package? Have you tried #XmlElement?
#XmlRootElement(name = "someField")
#XmlType(propOrder = {"some_son_field_1", "some_son_field_2"})
public class someField {
#XmlElement(name="some_son_field_1")
private String someSonField1;
#XmlElement(name="some_son_field_2")
private String someSonField2;
//...
}
Not sure, probably you should try putting them on getters, as your fields are private.
Or you could use unify-jdocs, a library which I created to read and write JSON documents without using any POJO objects. Rather than defining POJO objects, which we know can be difficult to manage in case of complex documents and changes to the JSON document, just don't use them at all. Directly read and write paths in the JSON document. For example, in your snippet, you could read and write the fields as:
Document d = new JDocument(s); // where s is a JSON string
String s1 = d.getString("$.some_field.some_son_field_1");
String s2 = d.getString("$.some_field.some_son_field_2");
You could use a similar way to write to these paths as so:
d.setString("$.some_field.some_son_field_1", "val1");
d.setString("$.some_field.some_son_field_2", "val2");
This library offers a whole lot of other features which can be used to manipulate JSON documents. Features like model documents which lock the structure of documents to a template, field level validations, comparisons, merging documents etc.
Obviously, you would consider it only if you wanted to work without POJO objects. Alternatively, you could use it to read and write a POJO object using your own method.
Check it out on https://github.com/americanexpress/unify-jdocs.
I am using Morphia (ver 0.99) for my JSON to Pojo mapping to my MongoDB (ver 2.0). Streaming data between web-clients and my server works fine. But now I have a use-case where I don't know what pattern that is most appropriate. Can I use Morphia or MongoDB Java driver to achieve my requirements or do I need to use Jackson and JPA 2.2 notation.
Here is my use-case;
Invoke Morphia query on selected collection (MongoDB)
The use the resulting ArrayList of Pojos for business logic and presentation (Primefaces)
Also convert the resulting ArrayList of Pojo's to JSON array of objects, but remove Pojo properties in the conversions that is not needed in the web-client
Push the converted JSON to the web-client for presentation
Converting one Pojo is straight forward with Morphia, but how do I convert an array?
return morph.toDBObject(obj).toString();
Is there a notation like #JsonIgnore in Morphia to ignore conversions to and from JSON ?
How can I most efficiently (without using more libraries if possible) to solve step three in in my use-case. Convert ArrayList to JSON and ignore conversion of some of the Pojo properties?
I've come up with a solution to my problem. It's maybe not the most elegant but it works the way I want and I don't have to include other libraries (like Gson and Jackson) to de-serialize my array list of Pojo's to Json, I only used classes from the MongoDB Java driver and the Morphia API. I also added a simple parameter list to strip away unnecessary property value to be pushed to the client.
public static String deserializeToJSON(List<?> objList, String... removeAttributes) {
List<DBObject> dbObjList = new ArrayList<>(objList.size());
DBObject dbObj;
for(Object obj :objList){
dbObj = morph.toDBObject(obj);
for(int i=0; i < removeAttributes.length; i++){
debug("Removed DBObject filed: " +dbObj.removeField(removeAttributes[i]));
}
dbObjList.add(dbObj);
}
String json = JSON.serialize(dbObjList);
return json;
}
I am using struts2 with hibernate. Does anyone know if it is possible to return query result as XML instead of ArrayList of domain objects?
Hibernate by default maps and persists a database record thought POJO , but in fact it also supports persisting , mapping and representing a database record in XML by using an experimental features called Dynamic models.
For example , to output a record in XML:
/**Get the a new session that is in the DOM4J EntityMode**/
Session dom4jSession = session.getSession(EntityMode.DOM4J);
Element outputXML=(Element) dom4jSession.get(Employee.class, employeeId);
XMLWriter writer = new XMLWriter( System.out, OutputFormat.createPrettyPrint() );
writer.write( outputXML);
To configure the format of the outputted XML , you can only do it by mapping the entity in XML . AFAIK ,there are no annotation equivalent .
Hibernate is an Object-Relational Mapper, meaning it maps a Relational database to objects. You want to use Hibernate to return an object and then use an XML Serializer to convert to XML.
The Simple Serializer is probably the best one to get started with. The Website contains a lot of tutorials and examples.
http://simple.sourceforge.net/
However there are a ton of XML Serializers for Java:
http://karussell.wordpress.com/2009/09/03/xml-serializers-for-java/
Maybe you could, once you have the result use XStream to parse the entire result to XML. A simple tutorial on XStream is available here.
In Jersey RESTful frame work, I know I can get xml data in client as following:
private static final String BaseURI = "http://DOMAN.com";
ClientConfig config = new DefaultClientConfig();
Client client = Client.create(config);
WebResource service = client.resource(BaseURI);
String xmlData = service.path("rest").path("todos").accept(
MediaType.APPLICATION_XML).get(String.class)
My question is how can I parse the xmlData then? I would like to get the needed data from xmlData, and transfer the needed data to JSON, what is the best way to implement this?
As a general rule, NEVER convert straight from XML to JSON (or vice versa) if you do not have to.
Rather, bind data from XML or JSON to POJOs, then do the other conversion. While it may seem non-intuitive this results in cleaner result and less problems, since conversions between POJOs and data formats have much more options, mature, well-designed libs; and POJOs are easier to configure (with annotations) and have more metadata to guide conversion process.
Direct conversions libs (like Jettison, see below) are plagued with various issues; often producing "franken-JSON", JSON that is technically correct but looks alien because of added constructs needed by conversion.
In case of Jersey, then, use JAXB for XML to/from POJOs, and Jackson for doing the same with JSON. These are libraries Jersey uses anyway; and direct usage is quite easy.
If you absolutely insist on direct conversion, you could try Jettison, but be prepared to hit a problem with Lists, arrays and Maps, if you need them (esp. single-element arrays -- arrays are problematic with XML, and auto-conversion often goes wrong).
If your service doesn't provide JSON as an option already (what happens if you change MediaType.APPLICATION_XML to MediaType.APPLICATION_JSON?), then I believe you have a few options, which I list in order of my preference.
Option 1: You have an XML schema for the the data
If you have an XML schema for the returned XML, you could use xjc to generate the JAXB annotated java classes and then leverage jackson to convert the instances to JSON data. I think this will get you going fast by leveraging this libraries over doing the parsing youself. Jackson is a robust library, used by glassfish for their Jersey(JAX-RS) implementation and I don't feel there is any risk in depending on this library.
Option 2: Use the json.org library, but I've had significant problem with this library having to do with its reflection-based methodology, etc. That said, it might work well for you...and you can test relatively easily and see if it does meet your requirements. If so...you're done! =)
Option 3: You don't have the XML schema and/or you want more control
as #Falcon pointed out, you can always use traditional XML parsing technologies to parse the XML into whatever you want. I'm partial to SAX parsing, but DOM could work depending on xml side
Regards,
Steve
Simplest and easiest way would be using org.json package : http://json.org/javadoc/org/json/XML.html
XML.toJSONObject(xmlData).toString()
Just this one line apart from necessary import statement will do it all.
Now that i have mentioned org.json library, lot of people may comment bad about it. Remember, I have said the simplest and easiest way, not the best or the most performant way ;-)
In case you are using maven, add this dependency :
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20090211</version>
</dependency>
Do you have any access to the "lower level interface" that generates the XML? If you do, the only change needed is to have the xml objects annotated with "#XmlRootElement". Then, you can just pass back the XMLobject as JSON without any further code.
Check Jsonix. If you have an XML schema, you can generate XML-JSON mappings and unmarshal/marshal XML in JavaScript. Very similar to JAXB (which Steve Siebert mentioned), but works on client.
// The PO variable provides Jsonix mappings for the purchase order test case
// Its definition will be shown in the next section
var PO = { };
// ... Declaration of Jsonix mappings for the purchase order schema ...
// First we construct a Jsonix context - a factory for unmarshaller (parser)
// and marshaller (serializer)
var context = new Jsonix.Context([ PO ]);
// Then we create an unmarshaller
var unmarshaller = context.createUnmarshaller();
// Unmarshal an object from the XML retrieved from the URL
unmarshaller.unmarshalURL('/org/hisrc/jsonix/samples/po/test/po-0.xml',
// This callback function will be provided with the result
// of the unmarshalling
function(result) {
// We just check that we get the values we expect
assertEquals('Alice Smith', result.value.shipTo.name);
assertEquals('Baby Monitor', result.value.item[1].productName);
});