I have a Word File look like this:
You don't need to understand the content, just take a look to my placeholders <env> and <applikationsabkürzung>. There are 10 pages with these placeholders and now I should replace them with other content. The black and yellow box are company pictures which I won't share.
Now I started to read the whole docx4j doc and generate after some time the following code:
public void manipulateWord(String path, String env, String appl) {
try {
WordprocessingMLPackage wpml = WordprocessingMLPackage.load(new File(path));
MainDocumentPart mdp = wpml.getMainDocumentPart();
List<Object> content = mdp.getContent();
// Include all Strings to replace
HashMap<String, String> mappings = new HashMap<String, String>();
mappings.put("<env>", env);
mappings.put("<applikationsabkürzung>", appl);
for (Object object : content) {
Text textElement = (Text) object;
String textToReplace = textElement.getValue();
if (mappings.keySet().contains(textToReplace)) {
textElement.setValue(mappings.get(textToReplace));
}
}
wpml.save(new File("C:\\Users\\kristina\\Desktop\\outputfile.docx"));
} catch (Docx4JException e) {
LOG.error(e);
}
Some explanaition:
String path is the path of the file in the picture above
String env is the value which should replace <env>
String appl is the value which should replace <applikationsabkürzung>
But when I run the method, nothing happen, my console just print just some infos. If they're important, i'll edit the post, but i don't think so.
So where is my fault? Would it work like that? I'm shortly before to despair...
MainDocumentPart.getContent() will return all OpenXml components within the main document flow (things like headers and footers have their own elements). Your code is assuming that the result of List<Object> content will be a collection of Text elements, which is not necessarily the case. For example, a typical (simple) document structure would be like this:
P // Paragraph element
-> R // Run element
-> Text // Text element
… so getContent() is, in all likelihood, going to spit out a load of P objects for a start.
There are a few ways to traverse docx4 files -- see the main docx4j site for more -- but one approach is shown in the method below. You can pass in MaindocumentPart as the first Object, and Text.class as the object type to search for. This should then assist in identifying all Text elements which contain one of your mapping values:
public List<Object> getAllElementFromObject(Object obj, Class<?> toSearch) {
List<Object> result = new ArrayList<Object>();
if (obj instanceof JAXBElement)
obj = ((JAXBElement<?>) obj).getValue();
if (obj.getClass().equals(toSearch))
result.add(obj);
else if (obj instanceof ContentAccessor) {
List<?> children = ((ContentAccessor) obj).getContent();
for (Object child : children) {
result.addAll(getAllElementFromObject(child, toSearch));
}
}
return result;
}
Related
I have two lists of type List<A> and List<B>. Now, what I'm trying to do is to create a List<C>, the values of which would be read from other two lists. I tried and implemented something like this.
public void fileUpload(int customerId) throws SQLException, IOException {
myList = myTiedosto.getFileDetails(customerId);
attachmentDetails = myAttachment.getAttachmentDetails(customerId);
List<Upload> uploadList = new ArrayList<>();
for (Attachment attachment : attachmentDetails) {
Upload upload = new Upload();
upload.setUuid(attachment.getUuid());
for (Tiedosto tiedosto : myList) {
upload.setCustomerId(tiedosto.getCustomerId());
upload.setFileName(tiedosto.getFileName());
upload.setFileSize(tiedosto.getFileSize());
upload.setContent(tiedosto.getContent());
}
uploadList.add(upload);
}
for (Upload myUploadList : uploadList) {
System.out.println(myUploadList.getCustomerId()+" " +myUploadList.getFileName()+" "+myUploadList.getUuid()+" "+myUploadList.getFileSize());
}
}
When I run this, myUploadList returns the same entry twice.
output
I'm a newbie in Java, I'd really appreciate some help.
Based on your comment, you should create and add the Upload instance to the output List inside the inner loop:
for (Attachment attachment : attachmentDetails) {
for (Tiedosto tiedosto : myList) {
Upload upload = new Upload();
upload.setUuid(attachment.getUuid());
upload.setCustomerId(tiedosto.getCustomerId());
upload.setFileName(tiedosto.getFileName());
upload.setFileSize(tiedosto.getFileSize());
upload.setContent(tiedosto.getContent());
uploadList.add(upload);
}
}
This will create an Upload instance from each pair of Attachment and Tiedosto.
EDIT:
Based on your latest comment, it would be better to use traditional for loop, since you want to create an Upload instance from each pair of Attachment and Tiedosto instances having the same index:
for (int i = 0 ; i < attachmentDetails.size(); i++) {
Tiedosto tiedosto = myList.get(i);
Upload upload = new Upload();
upload.setUuid(attachmentDetails.get(i).getUuid());
upload.setCustomerId(tiedosto.getCustomerId());
upload.setFileName(tiedosto.getFileName());
upload.setFileSize(tiedosto.getFileSize());
upload.setContent(tiedosto.getContent());
uploadList.add(upload);
}
After reading your comments, I think this is what you want.
int i = 0;
for (Attachment attachment : attachmentDetails) {
Upload upload = new Upload();
upload.setUuid(attachment.getUuid());
upload.setCustomerId(myList.get(i).getCustomerId());
upload.setFileName(myList.get(i).getFileName());
upload.setFileSize(myList.get(i).getFileSize());
upload.setContent(myList.get(i).getContent());
uploadList.add(upload);
i++;
}
or if you have a way of identifying which if the myList belongs to which attachment then you can loop through the myList and do a check:
for (Attachment attachment : attachmentDetails) {
Upload upload = new Upload();
upload.setUuid(attachment.getUuid());
for (Tiedosto tiedosto : myList) {
if(tiedosto.getCustomerId() == attachment.getId()//assuming you have this field
upload.setCustomerId(tiedosto.getCustomerId());
upload.setFileName(tiedosto.getFileName());
upload.setFileSize(tiedosto.getFileSize());
upload.setContent(tiedosto.getContent());
}
uploadList.add(upload);
}
The entries are not same as both have different UUID. The other details such as CustomerId,Filename, Filesize etc. are same as you are iterating the same myList for each iteration row of the list myAttachment. So, verify whether you are iterating correct with respect to the expected data
Man, It's a logical problem in your program.
I guess it's caused by the inner cycle.
It's overwriting the upload with the last element myList's information. Maybe that's why you have exactly the same information in all the fields, except for the uuid.
I want to have a changefeed on one attribute of my object in rethinkdb in the java language.
I tried this:
Cursor curs = r.db("mytestdb").
table("tennis").
get(Constants.WORKING_PROJECT_ID).
getField("time").
changes().
run(conn);
for (Object doc : curs) {
System.out.println(doc);
}
but I get this com.rethinkdb.gen.exc.ReqlQueryLogicError: Cannot convert STRING to SEQUENCE as an Exception.
Im really new to rethinkDB. Can someone help me ?
getField("time") gets particular field value, you can't subscribe on value.
That's what this com.rethinkdb.gen.exc.ReqlQueryLogicError: Cannot convert STRING to SEQUENCE says.
You can filter changes you want to get:
Cursor curs = r.db("mytestdb").
table("tennis").get(Constants.WORKING_PROJECT_ID)
.filter(row -> row.g("new_val").g("time").ne(row.g("old_val").g("time")))
.changes().run(conn);
for (Object doc : curs) {
}
According to the official documentation Update API - Upserts one can use scripted_upsert in order to handle update (for existing document) or insert (for new document) form within the script. The thing is they never show how the script should look to do that. The Java - Update API Doesn't have any information on the ScriptUpsert uses.
This is the code I'm using:
//My function to build and use the upsert
public void scriptedUpsert(String key, String parent, String scriptSource, Map<String, ? extends Object> parameters) {
Script script = new Script(scriptSource, ScriptType.INLINE, null, parameters);
UpdateRequest request = new UpdateRequest(index, type, key);
request.scriptedUpsert(true);
request.script(script);
if (parent != null) {
request.parent(parent);
}
this.bulkProcessor.add(request);
}
//A test call to validate the function
String scriptSource = "if (!ctx._source.hasProperty(\"numbers\")) {ctx._source.numbers=[]}";
Map<String, List<Integer>> parameters = new HashMap<>();
List<Integer> numbers = new LinkedList<>();
numbers.add(100);
parameters.put("numbers", numbers);
bulk.scriptedUpsert("testUser", null, scriptSource, parameters);
And I'm getting the following exception when "testUser" documents doesn't exists:
DocumentMissingException[[user][testUser]: document missing
How can I make the scriptUpsert work from the Java code?
This is how a scripted_upsert command should look like (and its script):
POST /sessions/session/1/_update
{
"scripted_upsert": true,
"script": {
"inline": "if (ctx.op == \"create\") ctx._source.numbers = newNumbers; else ctx._source.numbers += updatedNumbers",
"params": {
"newNumbers": [1,2,3],
"updatedNumbers": [55]
}
},
"upsert": {}
}
If you call the above command and the index doesn't exist, it will create it, together with the newNumbers values in the new documents. If you call again the exact same command the numbers values will become 1,2,3,55.
And in your case you are missing "upsert": {} part.
As Andrei suggested I was missing the upsert part, changing the function to:
public void scriptedUpsert(String key, String parent, String scriptSource, Map<String, ? extends Object> parameters) {
Script script = new Script(scriptSource, ScriptType.INLINE, null, parameters);
UpdateRequest request = new UpdateRequest(index, type, key);
request.scriptedUpsert(true);
request.script(script);
request.upsert("{}"); // <--- The change
if (parent != null) {
request.parent(parent);
}
this.bulkProcessor.add(request);
}
Fix it.
I'm using Groovy's StreamingMarkupBuilder to generate XML dynamically based on the results of a few SQL queries. I'd like to call a method from inside of the closure but the markup builder tries to create an XML node using the method name.
Here's an example of what I'm trying to do:
Map generateMapFromRow(GroovyRowResult row) {
def map = [:]
def meta = row.getMetaData()
// Dynamically generate the keys and values
(1..meta.getColumnCount()).each { column -> map[meta.getColumnName(column)] = row[column-1] }
return map
}
def sql = Sql.newInstance(db.url, db.user, db.password, db.driver)
def builder = new StreamingMarkupBuilder()
def studentsImport = {
students {
sql.eachRow('select first_name, middle_name, last_name from students') { row ->
def map = generateMapFromRow(row) // Here is the problem line
student(map)
}
}
}
println builder.bind(studentsImport).toString()
This will generate XML similar to the following:
<students>
<generateMapFromRow>
[first_name:Ima, middle_name:Good, last_name:Student]
</generateMapFromRow>
<student/>
<generateMapFromRow>
[first_name:Ima, middle_name:Bad, last_name:Student]
</generateMapFromRow>
<student/>
</students>
I've tried moving the method out to a class and calling to statically on the class, which doesn't work also.
Due to the nature of how StreamingMarkupBuilder works, I'm afraid that it isn't actually possible to do this, but I'm hoping that it is.
I may loose smth during example simplification, but such code will work.
In your example students is a closure call, so it may mess smth inside.
def builder = new groovy.xml.StreamingMarkupBuilder()
def generateMapFromRow = { ["$it": it] }
builder.bind {
10.times {
def map = generateMapFromRow(it) // Now closure is escaped, there is local variable with such name.
student(map)
}
}
As said here: http://groovy.codehaus.org/Using+MarkupBuilder+for+Agile+XML+creation
Things to be careful about when using markup builders is not to overlap variables you currently have in scope. The following is a good example
import groovy.xml.MarkupBuilder
def book = "MyBook"
def writer = new StringWriter()
def xml = new MarkupBuilder(writer)
xml.shelf() {
book(name:"Fight Club") { // Will produce error.
}
}
println writer.toString()
Builder's work similar to MethodMissing captors, ans if there is local variable in scope, no node will be produced.
I have a XmlDocument in java, created with the Weblogic XmlDocument parser.
I want to replace the content of a tag in this XMLDocument with my own data, or insert the tag if it isn't there.
<customdata>
<tag1 />
<tag2>mfkdslmlfkm</tag2>
<location />
<tag3 />
</customdata>
For example I want to insert a URL in the location tag:
<location>http://something</location>
but otherwise leave the XML as is.
Currently I use a XMLCursor:
XmlObject xmlobj = XmlObject.Factory.parse(a.getCustomData(), options);
XmlCursor xmlcur = xmlobj.newCursor();
while (xmlcur.hasNextToken()) {
boolean found = false;
if (xmlcur.isStart() && "schema-location".equals(xmlcur.getName().toString())) {
xmlcur.setTextValue("http://replaced");
System.out.println("replaced");
found = true;
} else if (xmlcur.isStart() && "customdata".equals(xmlcur.getName().toString())) {
xmlcur.push();
} else if (xmlcur.isEnddoc()) {
if (!found) {
xmlcur.pop();
xmlcur.toEndToken();
xmlcur.insertElementWithText("schema-location", "http://inserted");
System.out.println("inserted");
}
}
xmlcur.toNextToken();
}
I tried to find a "quick" xquery way to do this since the XmlDocument has an execQuery method, but didn't find it very easy.
Do anyone have a better way than this? It seems a bit elaborate.
How about an XPath based approach? I like this approach as the logic is super-easy to understand. The code is pretty much self-documenting.
If your xml document is available to you as an org.w3c.dom.Document object (as most parsers return), then you could do something like the following:
// get the list of customdata nodes
NodeList customDataNodeSet = findNodes(document, "//customdata" );
for (int i=0 ; i < customDataNodeSet.getLength() ; i++) {
Node customDataNode = customDataNodeSet.item( i );
// get the location nodes (if any) within this one customdata node
NodeList locationNodeSet = findNodes(customDataNode, "location" );
if (locationNodeSet.getLength() > 0) {
// replace
locationNodeSet.item( 0 ).setTextContent( "http://stackoverflow.com/" );
}
else {
// insert
Element newLocationNode = document.createElement( "location" );
newLocationNode.setTextContent("http://stackoverflow.com/" );
customDataNode.appendChild( newLocationNode );
}
}
And here's the helper method findNodes that does the XPath search.
private NodeList findNodes( Object obj, String xPathString )
throws XPathExpressionException {
XPath xPath = XPathFactory.newInstance().newXPath();
XPathExpression expression = xPath.compile( xPathString );
return (NodeList) expression.evaluate( obj, XPathConstants.NODESET );
}
How about an object oriented approach? You could deserialise the XML to an object, set the location value on the object, then serialise back to XML.
XStream makes this really easy.
For example, you would define the main object, which in your case is CustomData (I'm using public fields to keep the example simple):
public class CustomData {
public String tag1;
public String tag2;
public String location;
public String tag3;
}
Then you initialize XStream:
XStream xstream = new XStream();
// if you need to output the main tag in lowercase, use the following line
xstream.alias("customdata", CustomData.class);
Now you can construct an object from XML, set the location field on the object and regenerate the XML:
CustomData d = (CustomData)xstream.fromXML(xml);
d.location = "http://stackoverflow.com";
xml = xstream.toXML(d);
How does that sound?
If you don't know the schema the XStream solution probably isn't the way to go. At least XStream is on your radar now, might come in handy in the future!
You should be able to do this with query
try
fn:replace(string,pattern,replace)
I am new to xquery myself and I have found it to be a painful query language to work with, but it does work quiet well once you get over the initial learning curve.
I do still wish there was an easier way which was as efficient?