Suppose I have an annotated Java class:
#XmlRootElement
class Person {
public String name;
public int age;
}
Instances of that class should be created from an XML document. But sometimes XML record does not match well to my POJOs. For example, it may has multiple <name> tags:
<person>
<name>Dr. Jekyll</name>
<name>Mr. Hyde</name>
<age>35</age>
</person>
In such cases I expect to get an error message from unmarshaller. But it actually just quietly re-initializes the property twice. Is there a way to make JAXB notify me about those errors? (Something like VerificationErrorHandler would be a perfect solution. Throwing an exception from the property setter looks too boiler-platy for me)
UPD. My JAXB setup contains no surprise:
var jaxb = JAXBContext.newInstance(Person.class);
var jaxbDecoder = jaxb.createUnmarshaller();
var errorLog = new ArrayList<ValidationEvent>();
jaxbDecoder.setEventHandler(e -> errorLog.add(e));
// Now unmarshal:
var p = (Person) jaxbDecoder.unmarshal(xml);
After that, errorLog is empty despite my XML document is obviously incorrect.
Related
I am generating a custom XML export in DSpace 5.2. The item that is to be exported as an XML file has an array of metadata values. The values must appear in the XML file as the given XSD file defines their hierarchy. I add the values based on the XSD order into the XML, but some XML tags are in an order different from the insertion order.
More details
The approach I am using is, at first, move the array of metadata values into a map. The keys of the map are the metadata field names. Then, based on the XSD, I get an appropriate value from the map and generate an XML element like this:
import org.dspace.content.Metadatum;
import org.w3c.dom.Element;
import org.w3c.dom.Document;
public class DSpaceXML implements Serializable {
// A member variable
private Document doc;
// A DSpace built-in function used to export an item to an XML
public final void addItem(Item item) throws Exception {
// Initialize this.doc
Element rootElement = doc.createElement("root");
Element higherElement = doc.createElement("higher-element");
Element lowerElement = doc.createElement("lower-element");
insertMetadataAsChildOfElement(higherElement, "child-of-higher", "dc.childOfHigher");
rootElement.appendChild(higherElement);
insertMetadataAsChildOfElement(lowerElement, "child-of-lower", "dc.childOfLower");
rootElement.appendChild(lowerElement);
// stuff to generate other elements of the XML
}
private void insertMetadataAsChildOfElement(Element parentElement, String childElementName,
String key) {
Element childElement;
<Metadatum> metadatumList = (<Metadatum>) metadataMap.get(key);
childElement = createElement(childElementName, metadatum.value);
parentElement.appendChild(childElement);
}
private Element createElement(String name, String value) {
Element el = doc.createElement(name);
el.appendChild(doc.createTextNode(value));
return el;
}
}
I expect an XML like this:
<root>
<higher-element>
<child-of-higher>Value1</child-of-higher>
</higher-element>
<lower-element>
<child-of-lower>Value2</child-of-lower>
</lower-element>
<another-element-1/>
....
<another-element-n/>
</root>
What I get is like this (<lower-element> is before <higher-element>):
<root>
<lower-element>
<child-of-lower>Value2</child-of-lower>
</lower-element>
<another-element-1/>
....
<another-element-k/>
<higher-element>
<child-of-higher>Value1</child-of-higher>
</higher-element>
<another-element-k-plus-1/>
....
<another-element-n/>
</root>
I cannot figure out why this happens while rootElement.appendChild(higherElement) is called before rootElement.appendChild(lowerElement). Also, I would appreciate if someone let me know if my approach is the best one for generating an XML from an XSD.
I figured out that I had a bug in my code. Due to checking a lot of metadata values, many lines after the line rootElement.appendChild(lowerElement), I had a line rootElement.appendChild(higherElement), so it overrode the former hierarchy of XML elements. As a result <higher-element> appeared after <lower-element>. But about the second part of my question, I will be happy if someone would tell me about the best practices of generating an XML based on an XSD regarding the limitations of DSpace 5.
The request will be in the form of Alphanumeric characters.
For example the below request : for ID tag I need to pass this ID to database and process which will be call to Database for generating response and store it into JAXB class in LIST format.
I understand that I need to take the list of ID and iterate as a single request
<?xml version="1.0"?>
<LISTREQUEST>
<ID><WSDKJ554555244dfs></ID>
<ID><WSDKJ554555244dfs></ID>
<ID><WSDKJ554555244dfs></ID>
</LISTREQUEST>
Can someone provide me the logic in JAVA to implement for above XML request
PFB code example, here LISTREQUEST is JAXB class from XSD
LISTREQUEST ids= new LISTREQUEST();
Object [] id=ids.getID();
for(String list:id){
Let me know what logic here I can use in JAVA to get a single ID and process it, next take the second ID and process the same ... which will get store into an JAXB class for sending response.
In below format I have a JAXB class
public class Lists {
// ...
protected List<Integer> numbers;
// ...
public List<Integer> getID() {
if (numbers == null) {
numbers = new ArrayList<Integer>();
}
return this.numbers;
}
// ...
}
This question may have been answered before in some dark recess of the Interwebs, but I couldn't even figure out how to form a meaningful Google query to search for it.
So: Suppose I have a (simplified) XML document like so:
<root>
<tag1>Value</tag1>
<tag2>Word</tag2>
<tag3>
<something1>Foo</something1>
<something2>Bar</something2>
<something3>Baz</something3>
</tag3>
</root>
I know how to use JAXB to unmarshal this into a Java Object in the standard use cases.
What I don't know how to do is unmarshal tag3's contents wholesale into a String. By which I mean:
<something1>Foo</something1>
<something2>Bar</something2>
<something3>Baz</something3>
as a String, tags and all.
Use annotation #XmlAnyElement.
I've been looking for the same solution and I expected to find some annotation that prevents parsing dom and live it as it is, but did not find it.
Detail at:
Using JAXB to extract inner text of XML element
and
http://blog.bdoughan.com/2011/04/xmlanyelement-and-non-dom-properties.html
I added one cheking in method getElement(), otherwise we could get IndexOutOfBoundsException
if (xml.indexOf(START_TAG) < 0) {
return "";
}
For me it's quite strange behavior with this solution. method getElement() is called for every tag of your xml. The first call is for "Value", the second - "ValueWord", etc. It appends the next tag for previous
update:
I noticed that this approach works only for ONE occurence of tag that we want to parse to String. It's impossible to parse correctly the followint example:
<root>
<parent1>
<tag1>Value</tag1>
<tag2>Word</tag2>
<tag3>
<something1>Foo</something1>
<something2>Bar</something2>
<something3>Baz</something3>
</tag3>
</parent1>
<parent2>
<tag1>Value</tag1>
<tag2>Word</tag2>
<tag3>
<something1>TheSecondFoo</something1>
<something2>TheSecondBar</something2>
<something3>TheSecondBaz</something3>
</tag3>
</parent2>
"tag3" with parent tag "parent2" will contain parameters from the first tag (Foo, Bar, Baz) instead of (TheSecondFoo, TheSecondBar, TheSecondBaz)
Any suggestions are appreciated.
Thanks.
I have an utility method that might come in handy for you in that case. See if it helps. I made a sample code with your example:
public static void main(String[] args){
String text= "<root><tag1>Value</tag1><tag2>Word</tag2><tag3><something1>Foo</something1><something2>Bar</something2><something3>Baz</something3></tag3></root>";
System.out.println(extractTag(text, "<tag3>"));
}
public static String extractTag(String xml, String tag) {
String value = "";
String endTag = "</" + tag.substring(1);
Pattern p = Pattern.compile(tag + "(.*?)" + endTag);
Matcher m = p.matcher(xml);
if (m.find()) {
value = m.group(1);
}
return value;
}
I have an xml file as following and when the filePath2 is null or empty I want the value of that to be of filePath1's value. Is there a way in which I can achieve this through JAXB.
<file filePath1="C:/filePath">
<subFile name="Test">
<filePath2></filePath2>
</subFile>
<file/>
I don't want to hardcode the default value. If the value for filePath2 is null or blank("") I want to set the filePath1 attribute as the value of 'String filePath'. Is there a way to do it via a setter in JAXB?
Using plain Oracle JAXB I only see the possibility to implement that using an javax.xml.bind.Unmarshaller.Listener.
Implement that interface in your model class and perform the necessary checks in the afterUnmarshal(..) method.
There you can access the value of filePath1 and set (if necessary) it to filePath2.
Thanks for all your inputs, at the end I opted for a simpler solution; to update the setter where filePath2 is being called.
The JAXB part -
String filePath2;
#XmlElement(required = true)
public void setFilePath2(final String file) {
this.filePath2= file;
}
Where filePath is used -
if (filePath2 == null || filePath2.isEmpty()) {
setFilePath2(getFilePath1());
}
If you come across a bettr yet simple solution let me know.
If you can use annotations, than this should do the trick
...
private String foo;
#XmlElement(defaultValue="bar")
public String getFoo() {
return foo;
}
...
This is my XML Format , for which i am using STAX for parsing and putting them inside my java Object called as FormBean
<id>38400016</id>
<name>admin</name>
<Brd units="5" sold="15">
</Brd>
<Brd units="5" sold="15">
</Brd>
<Brd units="5" sold="15">
</Brd>
class FormBean
{
double units;
double sold;
String name;
String id ;
}
See the way i am doing parsing using STAX
if (startElementName.equals("Brd"))
{
FormBean formbean = new FormBean();
// Here i am getting the attributes from Brd and setting them into FormBean
// as shown in below way
formbean.units = attribute.getValue(); // sets the unit value into FormBean
}
if (startElementName.equals("name"))
{
}
Now my question is , how can i set the name and id variables also within the same FormBean as i cant create a new instance of FormBean inside a id or an name tags ??
And at last i am adding these FormBean to an arrayList .
Have you considered using a library like
XStream
JAXB
JiBX
To do all the heavy lifting for you?