Is there a good way to convert a document into JSON representation to then display on a web page? (It is a requirement that the document is converted to JSON)
My Idea if there isn't a built in way to do this is to represent the Run/Paragraph structure as JSON Objects, but I feel like this wouldn't work as well once I start working with more complex Word Documents.
If you add:
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.11.3</version>
</dependency>
you can try something like:
import org.docx4j.Docx4J;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
public class ConvertOutJSON {
static String inputfilepath = System.getProperty("user.dir") + "/sample-docs/sample-docxv2.docx";
public static void main(String[] args)
throws Exception {
WordprocessingMLPackage wordMLPackage
= Docx4J.load(new java.io.File(inputfilepath));
String xml = wordMLPackage.getMainDocumentPart().getXML();
//System.out.println(xml);
XmlMapper xmlMapper = new XmlMapper();
JsonNode node = xmlMapper.readTree(xml);
ObjectMapper jsonMapper = new ObjectMapper();
//String json = jsonMapper.writeValueAsString(node);
String json = jsonMapper.writerWithDefaultPrettyPrinter().writeValueAsString(node);
System.out.println(json);
}
}
However in a quick test, I noticed some w:p nodes were not being emitted as JSON. I haven't looked to see whether they get dropped by Jackson at the readTree step or when ObjectMapper writes its output; you'll need to dig into Jackson to fix that.
It is currently producing output like:
{
"Ignorable" : "w14 wp14",
"body" : {
"p" : {
"rsidR" : "00D15781",
"rsidRDefault" : "00D15781",
"pPr" : {
"ind" : {
"left" : "0"
}
}
},
"tbl" : {
"tblPr" : {
"tblStyle" : {
"val" : "TableGrid"
},
"tblW" : {
"w" : "0",
"type" : "auto"
},
"tblLook" : {
"firstRow" : "1",
"lastRow" : "0",
"firstColumn" : "1",
"lastColumn" : "0",
"noHBand" : "0",
"noVBand" : "1",
"val" : "04A0"
}
},
"tblGrid" : {
"gridCol" : {
"w" : "3561"
}
},
"tr" : {
"rsidR" : "00D15781",
"tc" : {
"tcPr" : {
"tcW" : {
"w" : "7122",
"type" : "dxa"
},
"gridSpan" : {
"val" : "2"
}
},
"p" : {
"rsidR" : "00D15781",
"rsidRDefault" : "00945132",
"pPr" : {
"ind" : {
"left" : "0"
}
},
"r" : {
"t" : "Horizontal merge"
}
}
}
}
},
"sectPr" : {
"rsidR" : "00D15781",
"headerReference" : {
"type" : "default",
"id" : "rId12"
},
"pgSz" : {
"w" : "11907",
"h" : "16839",
"code" : "9"
},
"pgMar" : {
"top" : "720",
"right" : "720",
"bottom" : "720",
"left" : "720",
"header" : "720",
"footer" : "720",
"gutter" : "0"
},
"cols" : {
"space" : "720"
},
"docGrid" : {
"linePitch" : "360"
}
}
}
}
I am new with Spring Data MongoDB and I am trying to implement an aggregation query in Java with Spring Data MongoDB. I have tried searching from this problem and approached it using MongoTemplate, but still to no result.
The format of my data:
[{
"_id" : ObjectId("5e1aea6c275360baf96bac29"),
"title" : "postim",
"upvotesBy" : [
"5e18b4c12753608718dfa007",
"5e19ac0f5161a4994ded1f35"
],
"file" : "test",
"description" : "description",
"postedBy" : "5e18b4c12753608718dfa007",
"createdAt" : ISODate("2020-01-12T09:44:12.119+0000"),
"_class" : "com.socialnetwork.post.Post"
},
{
"_id" : ObjectId("5e1aeaf8275360bb4bb47325"),
"title" : "postim2",
"upvotesBy" : [
"5e18b4c12753608718dfa007",
"5e19ac0f5161a4994ded1f35"
],
"file" : "test2",
"description" : "description2",
"postedBy" : "5e18b4c12753608718dfa007",
"createdAt" : ISODate("2020-01-12T09:46:32.909+0000"),
"_class" : "com.socialnetwork.post.Post"
}]
My query:
db.post.aggregate([
{
$match: {}
},
{
$lookup: {
from: "users",
localField: "postedBy",
foreignField: "_id",
as: "user"
}
},
{
$group: {
_id: {
username: "$user.name",
title: "$title",
description: "$description",
upvotes: { $size: "$upvotesBy" },
upvotesBy: "$upvotesBy",
isUpvoted: { $in: [req.query.userId, "$upvotesBy"] },
isPinned: {
$cond: {
if: { $gte: [{ $size: "$upvotesBy" }, 3] },
then: true,
else: false
}
},
file: "$file",
createdAt: {
$dateToString: {
format: "%H:%M %d-%m-%Y",
timezone: "+01",
date: "$createdAt"
}
},
id: "$_id"
}
}
},
{ $sort: { "_id.isPinned": -1, "_id.createdAt": -1 } }
])
This is the query I use in my Javascript backend and I can do this fairly easy with Mongoose. However I am having some difficulty with the Java implementation of it.
private LookupOperation getLookupOperation() {
return LookupOperation.newLookup().from("user")
.localField("postedBy")
.foreignField("_id")
.as("user");
}
#Override
public List<PostSummary> aggregate() {
LookupOperation lookupOperation = getLookupOperation();
return mongoTemplate.aggregate(Aggregation.newAggregation(lookupOperation, Aggregation.group("id")
.addToSet("user.name").as("username")
.addToSet("title").as("title")
.addToSet("description").as("description")
.addToSet("id").as("id")
.push("upvotesBy").as("upvotesBy")
.addToSet("file").as("file")
.addToSet("createdAt").as("createdAt")
), Post.class, PostSummary.class).getMappedResults();
}
When I try to run this I get the following error:
"Cannot convert [] of type class java.util.ArrayList into an instance of class java.lang.Object! Implement a custom Converter<class java.util.ArrayList, class java.lang.Object> and register it with the CustomConversions. Parent object was: com.socialnetwork.post.PostSummary#7159d908"
When I delete the .addToSet("user.name").as("username") from the group aggregation I also get an error from .push("upvotesBy").as("upvotesBy") as it can not convert [] of type class java.util.ArrayList into an instance of class java.lang.String
Also the implementation of the Post Class and the PostSummary Class is simple:
Post.java:
#Document
public class Post {
#Id
private String id;
private String title;
private List<String> upvotesBy;
private String file;
private String description;
private String postedBy;
private Date createdAt = new Date();
// ... Getters and Setters for each field
}
PostSummary.java:
public class PostSummary {
private String username;
private String title;
private String description;
private List<String> upvotesBy;
private String file;
private String createdAt;
private String id;
//... Getters and Setters for the class
}
I also need to implement the isUpvoted and isPinned part of the query, but getting the idea on how to approach the first problem would be a great start.
EDIT: My desired output:
[
{
"username" : "user1",
"title" : "postim2",
"upvotesBy" : [
"5e18b4c12753608718dfa007",
"5e19ac0f5161a4994ded1f35"
],
"file": "file1",
id: "5e18b4c12753608718dber01"
... Other fields of the original post
},
{
"username" : "user2",
"title" : "postim2",
"upvotesBy" : [
"5e18b4c12753608718dfa007",
"5e19ac0f5161a4994ded1f35"
],
id: "5e18b4c12753608718dber02",
"file": "file2",
... Other fields of the original post
}
]
So from the lookup operation I need only to get the name of the user.
Let's do it
We need to update your aggregation to make it work.
Errors:
users's _id is ObjectId type, but in your post you have stored as String, so $lookup should be changed to Uncorrelated sub-queries
We replace $group by '$addFields' which fits better
We add as last stage $project operator to exclude all unsed fields.
db.post.aggregate([
{
$match: {}
},
{
$lookup: {
from: "users",
let: {
postedBy: "$postedBy"
},
pipeline: [
{
$match: {
$expr: {
$eq: [
{
"$toString": "$_id"
},
"$$postedBy"
]
}
}
}
],
as: "user"
}
},
{
$unwind: "$user"
},
{
$addFields: {
id: {
$toString: "$_id"
},
username: "$user.name",
upvotes: {
$size: "$upvotesBy"
},
isUpvoted: {
$in: [
"5e18b4c12753608718dfa007",
"$upvotesBy"
]
},
isPinned: {
$cond: [
{
$gte: [
{
$size: "$upvotesBy"
},
3
]
},
true,
false
]
},
createdAt: {
$dateToString: {
format: "%H:%M %d-%m-%Y",
timezone: "+01",
date: "$createdAt"
}
}
}
},
{
$sort: {
"isPinned": -1,
"createdAt": -1
}
},
{
$project: {
_id: 0,
user: 0,
upvotesBy: 0,
_class: 0
}
}
])
Now, we transform this query to Spring-Data syntax.
Java Implementation
package postman;
import static org.springframework.data.mongodb.core.aggregation.Aggregation.match;
import static org.springframework.data.mongodb.core.aggregation.Aggregation.project;
import static org.springframework.data.mongodb.core.aggregation.Aggregation.sort;
import static org.springframework.data.mongodb.core.aggregation.Aggregation.unwind;
import java.util.Arrays;
import java.util.List;
import org.bson.Document;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
import org.springframework.data.mongodb.core.aggregation.AggregationOperationContext;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.stereotype.Service;
#Service
public class PostmanService {
#Autowired
private MongoTemplate mongoTemplate;
public List<PostSummary> find(String userId){
Aggregation aggregation = Aggregation.newAggregation(
match(new Criteria()),
//lookup("users", "postedBy", "_id", "user")
new AggregationOperation() {
#Override
public Document toDocument(AggregationOperationContext context) {
return new Document("$lookup",
new Document("from", "users")
.append("let", new Document("postedBy", "$postedBy"))
.append("pipeline", Arrays.asList(
new Document("$match",
new Document("$expr",
new Document("$eq", Arrays.asList(
new Document("$toString", "$_id"),
"$$postedBy"
))))))
.append("as", "user"));
}
},
unwind("$user"),
new AggregationOperation() {
#Override
public Document toDocument(AggregationOperationContext context) {
return new Document("$addFields",
new Document("id", new Document("$toString", "$_id"))
.append("username", "$user.name")
.append("upvotes", new Document("$size", "$upvotesBy"))
.append("isUpvoted", new Document("$in", Arrays.asList(userId, "$upvotesBy")))
.append("isPinned", new Document("$cond",
Arrays.asList(new Document("$gte",
Arrays.asList(new Document("$size", "$upvotesBy"), 3)), Boolean.TRUE, Boolean.FALSE)))
.append("createdAt", new Document("$dateToString",
new Document("format", "%H:%M %d-%m-%Y")
.append("timezone", "+01")
.append("date", "$createdAt")
)));
}
},
sort(Direction.DESC, "isPinned", "createdAt"),
project().andExclude("user", "_class")
);
System.out.println("Aggregation: " + aggregation.toString());
return mongoTemplate.aggregate(aggregation, mongoTemplate.getCollectionName(Post.class), PostSummary.class).getMappedResults();
}
}
Now, we call aggregation pipeline:
List<PostSummary> l = postmanService.find("5e18b4c12753608718dfa007");
for(PostSummary post: l) {
ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
System.out.println(ow.writeValueAsString(post));
}
2020-01-12 16:15:22.043 INFO 11148 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2020-01-12 16:15:22.047 INFO 11148 --- [ main] Postman.PostmanApplication : Started PostmanApplication in 4.602 seconds (JVM running for 5.301)
Aggregation: { "aggregate" : "__collection__", "pipeline" : [{ "$match" : {}}, { "$lookup" : { "from" : "users", "let" : { "postedBy" : "$postedBy"}, "pipeline" : [{ "$match" : { "$expr" : { "$eq" : [{ "$toString" : "$_id"}, "$$postedBy"]}}}], "as" : "user"}}, { "$unwind" : "$user"}, { "$addFields" : { "id" : { "$toString" : "$_id"}, "username" : "$user.name", "upvotes" : { "$size" : "$upvotesBy"}, "isUpvoted" : { "$in" : ["5e18b4c12753608718dfa007", "$upvotesBy"]}, "isPinned" : { "$cond" : [{ "$gte" : [{ "$size" : "$upvotesBy"}, 3]}, true, false]}, "createdAt" : { "$dateToString" : { "format" : "%H:%M %d-%m-%Y", "timezone" : "+01", "date" : "$createdAt"}}}}, { "$sort" : { "isPinned" : -1, "createdAt" : -1}}, { "$project" : { "user" : 0, "_class" : 0}}]}
2020-01-12 16:15:22.161 INFO 11148 --- [ main] org.mongodb.driver.connection : Opened connection [connectionId{localValue:2, serverValue:277}] to localhost:27017
{
"username" : "user1",
"title" : "postim2",
"description" : "description2",
"upvotesBy" : [ "5e18b4c12753608718dfa007", "5e19ac0f5161a4994ded1f35" ],
"file" : "test2",
"createdAt" : "10:46 12-01-2020",
"id" : "5e1aeaf8275360bb4bb47325"
}
{
"username" : "user1",
"title" : "postim",
"description" : "description",
"upvotesBy" : [ "5e18b4c12753608718dfa007", "5e19ac0f5161a4994ded1f35" ],
"file" : "test",
"createdAt" : "10:44 12-01-2020",
"id" : "5e1aea6c275360baf96bac29"
}
Fetch messages method
private void fetchMessages() {
rootRef.child("Messages").child(MessageSenderId).child(MessageRecieverId)
.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
Messages messages = dataSnapshot.getValue(Messages.class);
messagesList.add(messages);
messageAdapter.notifyDataSetChanged();
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
throw databaseError.toException();
}
});
}
Defining Ids
MessageSenderId = mAuth.getCurrentUser().getPhoneNumber();
MessageRecieverId = getIntent().getStringExtra("visit_user_id");
trying to make a basic chat app
Since only the senders id is called in rootref only the messages i send r being displayed in d recyclerview... im unable to receive messages because of this... how can i make it to retrieve recievers id and senders id also at the same time
Database strucutre
{
"Messages" : {
"+918105571584" : {
"+919945342730" : {
"-L58IPCLEeE21vH_-1Ry" : {
"message" : "Hi",
"seen" : false,
"time" : 1518427022478,
"type" : "text"
},
"-L58IU1VIHN0rHaUox3a" : {
"message" : "Hello",
"seen" : false,
"time" : 1518427042257,
"type" : "text"
},
"-L58IYN1GpHPdkWCY7Hn" : {
"message" : "Hi",
"seen" : false,
"time" : 1518427060021,
"type" : "text"
}
}
},
"+919945342730" : {
"+918105571584" : {
"-L58IPCLEeE21vH_-1Ry" : {
"message" : "Hi",
"seen" : false,
"time" : 1518427022478,
"type" : "text"
},
"-L58IU1VIHN0rHaUox3a" : {
"message" : "Hello",
"seen" : false,
"time" : 1518427042257,
"type" : "text"
},
"-L58IYN1GpHPdkWCY7Hn" : {
"message" : "Hi",
"seen" : false,
"time" : 1518427060021,
"type" : "text"
}
}
}
},
"Users" : {
"+918105571584" : {
"Email" : "",
"Name" : "Akash",
"Quote" : "",
"Status" : ""
},
"+919945342730" : {
"Email" : "",
"Name" : "Sav",
"Quote" : "",
"Status" : ""
}
}
}
According to your last comment, I understand that you can display only the messages that correpond to a single user. A user can see only his messages butd not other user messages. To solve this, you need to create chat rooms, in which every user can add messages that can be seen by all chat room members.
So for that, you need to change your database structure. You cannot achieve this, using the actual database.
I'm using Retrofit+Gson for parsing JSON.
When I try parse response from Google Places API (ok, I don't try parse, I just try to make model for this response) and I get some error.
This is response from Google Place API:
{
"predictions" : [
{
"description" : "Николаевская область, Украина",
"id" : "3bd747cc4efc2288da48942b909ce18a053c2060",
"matched_substrings" : [
{
"length" : 5,
"offset" : 0
}
],
"place_id" : "ChIJydRVsbqaxUARLq1R8Q3RgpM",
"reference" : "ClRPAAAAwseWiG8NUMt7TqSqz9rMP8R2M4rX7-cMRmIp4OCYL-VdRSr5B5T_PMwWzYOydVStVpYDvm0ldXYPEzxFAuvn1LqhtWHdROhsERwvmx0tVlwSEFdMw0sOe3rDaB2AqKKmF-YaFLvhiEOz3Bklv5-iTa7QQORILVCU",
"structured_formatting" : {
"main_text" : "Николаевская область",
"main_text_matched_substrings" : [
{
"length" : 5,
"offset" : 0
}
],
"secondary_text" : "Украина"
},
"terms" : [
{
"offset" : 0,
"value" : "Николаевская область"
},
{
"offset" : 22,
"value" : "Украина"
}
],
"types" : [ "administrative_area_level_1", "political", "geocode" ]
}, ...],
"status" : "OK"
}
This is my model for this response:
public class GetGoogleMapPlacesResponse {
#SerializedName("predictions")
private List<GooglePlace> googlePlaces;
public List<GooglePlace> getGooglePlaces() {
return googlePlaces;
}
public void setGooglePlaces(List<GooglePlace> googlePlaces) {
this.googlePlaces = googlePlaces;
}
}
But when Retrofit try's to parse response to model I get error:
java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be cast to com.myapp.net.rest.response.GetGoogleMapPlacesResponse
And this is raw response in Debug mode:
You're missing a constructor of GetGoogleMapPlacesResponse model.
public class GetGoogleMapPlacesResponse {
private List<GooglePlace> googlePlaces;
private String status;
public GetGoogleMapPlacesResponse(List<GooglePlace> googlePlaces, String status) {
this.googlePlaces = googlePlaces;
this.status = status;
}
...getters & setters
}
But i highly suggest you to use AutoValue with Gson extension and then your model will look like this :
#AutoValue
public abstract class GetGoogleMapPlacesResponse {
#SerializedName("predictions") public abstract List<GooglePlace> googlePlaces;
public abstract String status;
}
For more info look here : https://github.com/rharter/auto-value-gson
I have some documents in my MongoDB database.
Looks like this:
{ "_id" : { "$oid" : "5598d61b0cfb246b90daa3f7" }, "name" : "Sarah", "uuid" : "488f69e9-8070-40f0-8c0a-b5d0bd53bdfe", "createdDate" : { "$date" : 1436079643735 }, "istested" : false }
{ "_id" : { "$oid" : "5598d6260cfb2461d4ad4f98" }, "name" : "Omah", "uuid" : "93e572c0-8acd-4397-8487-4d458bbafa8d", "createdDate" : { "$date" : 1436079654217 }, "istested" : false }
{ "_id" : { "$oid" : "5598d6300cfb246bace63cef" }, "name" : "Secret", "uuid" : "60e1413e-49e3-4315-a970-7111d55fe8d1", "createdDate" : { "$date" : 1436079664902 }, "istested" : false }
Now I wan to get the name
where uuid = 93e572c0-8acd-4397-8487-4d458bbafa8d (Omah)
How do I do this? (Using com.mongodb.async)
To get the name where uuid = 93e572c0-8acd-4397-8487-4d458bbafa8d, use the find() method to query the collection, create a filter to pass to the find() method to get a subset of the documents in your collection, use the projection parameter in the Projections helper class for the find operation to limit the fields returned, and then call the first() method on the find() operation to return the first document or null rather than a cursor. For example,
import static com.mongodb.client.model.Projections.*;
import static com.mongodb.client.model.Filters.*;
Block<Document> printDocument = new Block<Document>() {
#Override
public void apply(final Document document) {
System.out.println(document.toJson());
}
};
collection.find(eq("uuid", "93e572c0-8acd-4397-8487-4d458bbafa8d"))
.projection(fields(include("name"), excludeId()))
.first(printDocument);
will print the document:
{ "name" : "Omah" }