Modify JsonNode of unknown JSON dynamically in Java - jackson

I am trying to modify a JSON (of an unknown structure) where the JsonPath and its equivalent XML Xpath is known to me.
I have tired using com.jayway.jsonpath.JsonPath library for the same.
The problem with JsonPath is, it returns me the value but I am not able to modify the Target Node.
Follows is my code snippet for the same
JsonPath.read(jsonFile, jsonPath);
JsonPath.parse(jsonPath);
System.out.println("Author: "+JsonPath.read(jsonFile, jsonPath));
I tried using Jackson as mentioned in previously asked quetion, But it needs to be traversed node by node as follows
((ObjectNode) parent).put(fieldName, newValue);
which I cannot do due to unknown structure.
I have tried the answer given to the question recursively parse JSON object but it says how to parse not modify
I need to do the follows
JsonNode root = mapper.readTree("Json in form of String");
((JsonNode)(root.get("JsonPath")).set("New Value");
Is there any way in which this can be achieved?

JsonNode objects are immutable so you can't modify them. What you can do is replace a JsonNode with another one. A cast to ObjectNode is also required to expose the required methods. First find the parent of the node you want to replace :
JsonNode node = root.findParent("JsonPath");
Then use either of these 2 methods to replace it with a new one:
((ObjectNode) node).remove("JsonPath"); // remove current node
((ObjectNode) node).put("JsonPath", "New Value"); // add new one with new value
or
((ObjectNode) node).replace("JsonPath", new TextNode("New Value"));

Related

Trying to Understand this function and Im completely lost

I have been trying to understand the following function and I think i am interpreting it incorrectly. I am trying to convert it to Python3
type UITreeNodeChild
= UITreeNodeChild UITreeNode
unwrapUITreeNodeChild : UITreeNodeChild -> UITreeNode
unwrapUITreeNodeChild child =
case child of
UITreeNodeChild node ->
node
I see the bottom part is that there is a function called unwrapUITreeNodeChild which has a arg called child which is of UITreeNodeChild and the function returns a UITreeNode.
In Python i see it as:
class UITreeNodeChild(Enum):
UITreeNodeChild=UITreeNode
def unwrapUITreeNodeChild(child: UITreeNodeChild) -> UITreeNode:
if child is UITreeNodeChild: #
pass # I have no idea what it really means.
I dont understand the rest of the function signature. After looking at the ELM docs, i was confused as the switch but it looks like it is passing node into the definition of UITreeNodeChild.
I know im wrong here, but i would love to be both correct and proper explained to me.
EDIT
So it is looking for a typecheck essentially in the sample, so what you would want to do is confirm that it is that type and then use this new property in underlying code. It would look somewhat similar to:
class UITreeNodeChild:
pass
def unwrapUITreeNodeChild(child: UITreeNodeChild) -> UITreeNode:
if type(child) == UITreeNodeChild:
pass
Note you want to check type not check the exact same object.
You are looking at a tagged union and a simple pattern match against that tagged union.
In type UITreeNodeChild = UITreeNodeChild UITreeNode, the UITreeNodeChild on the right side is the tag. UITreeNode is the data type that is tagged.
When you say:
case child of
UITreeNodeChild node ->
node
You are pattern matching child against a pattern that begins with UITreeNodeChild. The tagged data type value is bound to the name node and this is also what it is returned from the case expression for this patten match.
Since this tagged union has only one variant you also have the option to unpack it in the parameter declaration:
unwrapUITreeNodeChild : UITreeNodeChild -> UITreeNode
unwrapUITreeNodeChild (UITreeNodeChild node) =
node
The way you do this in python depends on the way you chose to implement the tagged union. The general method is the same. You have to have some kind of tag and you have to have a way to determine the tag of a value. You can then return the embedded data type based on the tag. With a single tag this becomes just returning the embedded data type.

Camunda: Test whether JSON array contains an element

I would like a conditional flow to depend on whether a JSON attribute (containing a JSON array of strings) contains a specific element.
The following expression works if the element is present, but throws an exception if it is not:
<bpmn:conditionExpression xsi:type="bpmn:tFormalExpression">
${ json_array.indexOf("foo")!=-1 }
</bpmn:conditionExpression>
The equivalent expression with lastIndexOf() also fails despite the documentation claiming that should not happen (issue 134).
Is there another way to do this?
This works, but seems rather clunky:
<bpmn:conditionExpression xsi:type="bpmn:tFormalExpression">
${ json_array.toString().contains("\"foo\"") }
</bpmn:conditionExpression>
I hope there is a better way.

Passing parameters in #FindBy Page factory

Is there any way so that we can parameterize the string which we pass to create a page object using a page factory?
ex:
String v = "password";
#FindBy(name=v)
private WebElementFacade password_Field;
I am trying to push the string v into the #FindBy but I am getting an error.
I am getting
The value for annotation attribute FindBy.name must be a constant expression.
Yes. It is possible. Please find the solution below:
In your Page, instead of using #FindBy, get the WebElementFacade using find method of the Page and then use it for operations.
For example: If you need to click on an element, please see the code below:
public void click(String elementId) {
WebElementFacade element = find(ById.id(elementId));
element.click();
}
This works perfectly for me. And the element id is coming all the way from .story examples.
No, Unfortunately, it's not possible to send arguments to annotation (reference). Since Java annotations does not allow dynamic parameterization. The compiler evaluates annotation metadata at compile time. So, it must be present. Though there are work around for this.
Check this- Java Annotations values provided in dynamic manner

Intellij idea plugin: Psi Classes

I am developing a plugin in Intellij idea. I am stuck at inserting a piece of code after a statement.
For eg:
mHelper.launchPurchaseflow(str,str,str);
I need to find the element launchPurchaseflow and add a piece of code after that statement.I used PsiElement and string matcher to get to the corresponding string.
Now my PsiElement = mhelper.launchPurchaseFlow.
If I use
psiClass.addAfter(newElement, PsiElement.getContext());
It is only trying to add after the closing paranthesis.So its pop out my new element as Incorrect Statement.
For eg:
Its adding at position
mHelper.launchPurchaseflow(str,str,str)//newElement;
But I need to add at
mHelper.launchPurchaseflow(str,str,str); // new Element.
It's hard to give you a correct answer since you're not showing us how you are inserting the new text, but let's say you have a variable PsiElement myElement that contains the expression mHelper.launchPurchaseflow(str,str,str) (without the semicolon).
If you call getParent() on that element, you will likely have access to the statement representing the method call:
// Should represent "mHelper.launchPurchaseflow(str,str,str);" with the semicolon
PsiElement parent = myElement.getParent();
If you add your code after this node, it should produce a valid statement.
Alternatively, if getParent() does not return a PsiStatement and you want to be certain to have the full statement, you can do this:
PsiElement statement = PsiTreeUtil.getParentOfType(myElement, PsiStatement.class);

Why does Javassist insist on looking for a default annotation value when one is explicitly specified?

I am using Javassist to add and modify annotations on a package-info "class".
In some cases, I need to deal with the following edge case. Someone has (incorrectly) specified an #XmlJavaTypeAdapters annotation on the package-info package, but has not supplied a value attribute (which is defined as being required). So it looks like this:
#XmlJavaTypeAdapters // XXX incorrect; value() is required, but javac has no problem
package com.foobar;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapters;
In Javassist, this comes through slightly oddly.
The javassist.bytecode.annotation.Annotation representing the #XmlJavaTypeAdapters annotation does not have a member value (getMemberValue("value") returns null), as expected.
It is of course possible to add a value() member value, and that is what I've done:
if (adaptersAnnotation.getMemberValue("value") == null) {
final ArrayMemberValue amv = new ArrayMemberValue(new AnnotationMemberValue(constantPool), constantPool);
adaptersAnnotation.addMemberValue("value", amv);
annotationsAttribute.addAnnotation(adaptersAnnotation);
}
In the code snippet above, I've created a new member value to hold an array of annotations, because the value() attribute of #XmlJavaTypeAdapters is an array of #XmlJavaTypeAdapter. I've specified its array type by trying to divine the Zen-like documentation's intent—it seems that if you supply another MemberValue that this MemberValue will somehow serve as the array's type. In my case I want the type of the array to be #XmlJavaTypeAdapter, which is an annotation, so the only kind of MemberValue that seemed appropriate was AnnotationMemberValue. So I've created an empty one of those and set it as the array type.
This works fine as far as it goes, as long as you stay "within" Javassist.
However, something seems to have gone wrong. If I ask Javassist to convert all of its proprietary annotations into genuine Java java.lang.annotation.Annotations, then when I try to access the value() attribute of this #XmlJavaTypeAdapters annotation, Javassist tells me that there is no default value. Huh?
In other words, that's fine—indeed there is not—but I have specified what I had hoped was a zero-length array (that is, the default value shouldn't be used; my explicitly specified zero-length array should be used instead):
final List<Object> annotations = java.util.Arrays.asList(packageInfoClass.getAnnotations());
for (final Object a : annotations) {
System.out.println("*** class annotation: " + a); // OK; one of these is #XmlJavaTypeAdapters
System.out.println(" ...of type: " + a.getClass()); // OK; resolves to XmlJavaTypeAdapters
System.out.println(" ...assignable to java.lang.annotation.Annotation? " + java.lang.annotation.Annotation.class.isInstance(a)); // OK; returns true
if (a instanceof XmlJavaTypeAdapters) {
final XmlJavaTypeAdapters x = (XmlJavaTypeAdapters)a;
System.out.println(" ...value: " + java.util.Arrays.asList(x.value())); // XXX x.value() throws an exception
}
}
So why is Javassist looking for a default value in this case?
My larger issue is of course to handle this (unfortunately somewhat common) case where #XmlJavaTypeAdapters is specified with no further information on it. I need to add a value member value that can hold an array of #XmlJavaTypeAdapter annotations. I can't seem to figure out how to accomplish this with Javassist. As always, all help appreciated.
For posterity, it appears that in this particular case (to avoid a NullPointerException and/or a RuntimeException), you need to do this:
if (adaptersAnnotation.getMemberValue("value") == null) {
final ArrayMemberValue amv = new ArrayMemberValue(constantPool);
amv.setValue(new AnnotationMemberValue[0]);
adaptersAnnotation.addMemberValue("value", amv);
annotationsAttribute.addAnnotation(adaptersAnnotation);
}
Note in particular that I deliberately omit the array type when building the ArrayMemberValue (including one of any kind will result in an exception). Then I explicitly set its value to an empty array of type AnnotationMemberValue. Any other combination here will result in an exception.
Additionally, and very oddly, the last line in that if block is critical. Even though in this particular case the annotation itself was found, and so hence was already present in the AnnotationsAttribute, you must re-add it. If you do not, you will get a RuntimeException complaining about the lack of a default value.
I hope this helps other Javassist hackers.