I'm trying to create a SAML response. One of the attributes that makes up the assertion is called address and the attribute value needs to be a custom type that is defined in an XSD. How do I add custom attribute value types to the response?
If your attribute value XML is in String form:
String yourXMLFragment = "...";
AttributeStatementBuilder attributeStatementBuilder =
(AttributeStatementBuilder) builderFactory.getBuilder(AttributeStatement.DEFAULT_ELEMENT_NAME);
AttributeStatement attributeStatement = attributeStatementBuilder.buildObject();
AttributeBuilder attributeBuilder =
(AttributeBuilder) builderFactory.getBuilder(Attribute.DEFAULT_ELEMENT_NAME);
Attribute attr = attributeBuilder.buildObject();
attr.setName("yourAttributeName");
XSAnyBuilder sb2 = (XSAnyBuilder) builderFactory.getBuilder(XSAny.TYPE_NAME);
XSAny attrAny = sb2.buildObject(AttributeValue.DEFAULT_ELEMENT_NAME, XSAny.TYPE_NAME);
attrAny.setTextContent(yourXMLFragment.trim());
attr.getAttributeValues().add(attrAny);
attributeStatement.getAttributes().add(attr);
Actually this above does not yeld correct results. The above example can be used only to create xsany with text content not xml content (xml content gets escaped).
So after digging in opensaml sources the following did work as needed:
public XSAny createXSAny(Element dom)
{
XSAnyBuilder anyBuilder = (XSAnyBuilder) Configuration.getBuilderFactory().getBuilder(XSAny.TYPE_NAME);
XSAny any = anyBuilder.buildObject(AttributeValue.DEFAULT_ELEMENT_NAME, XSAny.TYPE_NAME);
// this builds only the root element not the whole dom
XSAny xo=anyBuilder.buildObject(dom);
// set/populate dom so whole dom gets into picture
xo.setDOM(dom);
any.getUnknownXMLObjects().add(xo);
return any;
}
Related
I am trying to understand how i can dynamically add fields in my document.
I am following the doc . However i'd like to use the addField method inside a "for" statement and thus programmatically generate fields.
I've tried this :
Builder document = Document.newBuilder();
document.setId(id);
// for statement start
com.google.appengine.api.search.Field.Builder field = Field.newBuilder().setName(key).setAtom(value);
document.addField(field);
// for statement end
document.build();
But it doesn't seems to work. When i'm trying i got this printed out
com.google.appengine.api.search.Document$Builder#2604ed54
Whereas when i use the original code :
String myDocId = "PA6-5000";
Document doc = Document.newBuilder()
// Setting the document identifer is optional.
// If omitted, the search service will create an identifier.
.setId(myDocId)
.addField(Field.newBuilder().setName("content").setText("the rain in spain"))
.addField(Field.newBuilder().setName("email").setText(userEmail))
.addField(Field.newBuilder().setName("domain").setAtom(userDomain))
.addField(Field.newBuilder().setName("published").setDate(new Date()))
.build();
I get the expected result with the code above.
How i can manipulate this in order to put addfield() into for statement ?
thanks for helping.
The docs imply the Document.Builder is mutable, but typically you should reassign the builder after each invocation in case it isn't.
Builder document = Document.newBuilder();
document.setId(id);
com.google.appengine.api.search.Field.Builder field = Field.newBuilder().setName(key).setAtom(value);
document = document.addField(field);
document.build();
In your case what you're seeing is the toString() method on the builder, not the document itself. The problem is the last line.
Try this:
Builder builder = Document.newBuilder();
builder.setId(id);
com.google.appengine.api.search.Field.Builder field = Field.newBuilder().setName(key).setAtom(value);
builder = builder.addField(field);
Document document = builder.build();
Then you can just drop in your for loop and away you go.
I'm using this code to sign a xml document:
Document doc = getDocument(xml_to_sign);
Element elemToSign = doc.getDocumentElement();
String file_uri_path = elemToSign.getBaseURI();
DataObjectDesc obj1 = new DataObjectReference(file_uri_path).withType("http://www.gzs.si/shemas/eslog/racun/1.5#Racun");
SignedDataObjects dataObjs = new SignedDataObjects(obj1);
signer.sign(dataObjs, elemToSign);
xml_to_sign is the full path to the xml file.
The problem is, that I would like to sign only the node with the id "data" (#data), but append the signature to the node elemToSign.
Is it possible to do this with xades4j?
Yes, it is. The sign method's argument is the parent node, not the element to sign (it could be the same node, depending on the configured references). In your example you should add a reference for "#data":
Document doc = getDocument(xml_to_sign);
Element parent = doc.getDocumentElement();
DataObjectDesc obj1 = new DataObjectReference("#data").withType("http://www.gzs.si/shemas/eslog/racun/1.5#Racun");
SignedDataObjects dataObjs = new SignedDataObjects(obj1);
signer.sign(dataObjs, parent);
Another option is to add a reference for the whole XML document (empty URI) and use a XPath transform.
You should specify that the attribute named "Id" in your xml document is the XML ID attribute that Apache Santuario (used internally by Xades4j) will use in the getElementById() (as lgoncalves has pointed out in his commments to his own answer).
Element parent = doc.getDocumentElement();
parent.setIdAttribute("Id", true);
//or parent.setIdAttributeNS("http://your.name.space", "Id", true);
I had the same problem, and this additional line of code solved it.
In my current project, we are in the process of re-factoring a java class that constructs an XML document. In previous versions of the product delivered to the customer, the XML document is built with lower case elements and attributes:
<rootElement attr = "abc">
<childElement childAttr = "xyz"/>
</rootElement>
But now we have a requirement to build the XML document with TitleCase element and attributes. The user will set a flag in a properties file to indicate whether the document should be built in lower case or title case. If the flag is configured to build the document in TitleCase, the resultant document will look like:
<RootElement Attr = "abc">
<ChildElement ChildAttr = "xyz">
</RootElement>
Various approaches to solve the problem:
1. Plugging in a transformer to convert lowercase XML document to TitleCase XML document. But this will impact the overall performance, as we deal with huge XML files spanning more than 10,000 lines.
2. Create two separate maps with corr. XML elements and attributes.
For eg:
lowercase map: rootelement -> rootElement, attr -> attr ....
TitelCase map: rootlement -> RootElement, attr -> Attr ....
Based on the property set by the user, the corr. map will be chosen and XML element/attributes from this map will be used to build the XML document.
3. Using enum to define constants and its corr. values.
public enum XMLConstants {
ROOTELEMENT("rootElement", "RootElement"),
ATTRIBUTE("attr", "Attr");
private String lowerCase;
private String titleCase;
private XMLConstants(String aLowerCase, String aTitleCase){
titleCase = aTitleCase;
lowerCase = aLowerCase;
}
public String getValue(boolean isLowerCase){
if(isLowerCase){
return lowerCase;
} else {
return titleCase;
}
}
}
--------------------------------------------------------------
// XML document builder
if(propertyFlag){
isLowerCase = false;
} else {
isLowerCase = true;
}
....
....
createRootElement(ROOTELEMENT.getValue(isLowerCase));
createAttribute(ATTRIBUTE.getValue(isLowerCase));
Please help me choose the right option keeping in mind the performance aspect of the entire solution. If you have any other suggestions, please let me know.
// set before generate XML
boolean isUpperCase;
// use function for each tag/attribute name instead of string constant
// smth. like getInCase("rootElement")
String getInCase(String initialName) {
String intialFirstCharacter = initialName.substring(0, 1);
String actualFirstCharacter;
if (isUpperCase) {
actualFirstCharacter = intialFirstCharacter.toUpperCase();
} else {
actualFirstCharacter = intialFirstCharacter.toLowerCase();
}
return actualFirstCharacter + initialName.substring(1);
}
I am writing a simulator which communicates with a client's piece of software over a local socket. The communication language is XML. I have written some code which works - parsing the incoming XML string into Document via the DocumentBuilder interface.
I have been encountering a problem with CDATA (Having never seen it before). Basically, I need to access fields within the CDATA tag and change them. I load up a 'template' XML document (to reply to the messages with) and use values received in the first message inside the response. Some of the fields that need to be changed are in this CDATA tag (clear what I mean below).
public static String getOutputMessage(String input) throws Exception{
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
Document inputDoc, outputDoc;
Element messageElement = (Element)inputDoc.getElementsByTagName("TRANS").item(0);
messageType = messageElement.getAttribute("name");
if (messageType.equals("processTransaction")){
outputDoc = db.parse(path+"processTransaction\\posPrintReceipt.xml");
outputDoc = changeContent(outputDoc, "PAN_NUMBER", transaction.getPan_number());
outputDoc = changeContent(outputDoc, "TOKEN", transaction.getToken());
outputDoc = changeContent(outputDoc, "TOTAL_AMOUNT", transaction.getTotal_amount());
outputDoc = changeContent(outputDoc, "TRANSACTION_TIME", transaction.getTransaction_time());
outputDoc = changeContent(outputDoc, "TRANSACTION_DATE", transaction.getTransaction_date());
}
}
private static Document changeContent(Document doc,String tag,String value) {
System.out.println("Changing: ["+tag+" : "+value+"]");
NodeList nodes=doc.getElementsByTagName(tag);
Node node = nodes.item(0);
Node parent=node.getParentNode();
node.setTextContent(value);
System.out.println(doc.getElementsByTagName(tag).item(0) + " " + node.getTextContent());
parent.replaceChild(node, doc.getElementsByTagName(tag).item(0));
return doc;
}
The functions above work on normal Elements but below is an example XML message I have to read and change some values such as
<RLSOLVE_MSG version="5.0">
<MESSAGE>
<SOURCE_ID>DP01</SOURCE_ID>
<TRANS_NUM>000001</TRANS_NUM>
</MESSAGE>
<POI_MSG type="interaction">
<INTERACTION name="posPrintReceipt">
<RECEIPT type="merchant" format="xml">
<![CDATA[<RECEIPT>
<AUTH_CODE>06130</AUTH_CODE>
<CARD_SCHEME>VISA</CARD_SCHEME>
<CURRENCY_CODE>GBP</CURRENCY_CODE>
<CUSTOMER_PRESENCE>internet</CUSTOMER_PRESENCE>
<FINAL_AMOUNT>1.00</FINAL_AMOUNT>
<MERCHANT_NUMBER>8888888</MERCHANT_NUMBER>
<PAN_NUMBER>454420******0382</PAN_NUMBER>
<PAN_EXPIRY>12/15</PAN_EXPIRY>
<TERMINAL_ID>04176421</TERMINAL_ID>
<TOKEN>454420bbbbbkqrm0382</TOKEN>
<TOTAL_AMOUNT>1.00</TOTAL_AMOUNT>
<TRANSACTION_DATA_SOURCE>keyed</TRANSACTION_DATA_SOURCE>
<TRANSACTION_DATE>14/02/2014</TRANSACTION_DATE>
<TRANSACTION_NUMBER>000001</TRANSACTION_NUMBER>
<TRANSACTION_RESPONSE>06130</TRANSACTION_RESPONSE>
<TRANSACTION_TIME>17:13:17</TRANSACTION_TIME>
<TRANSACTION_TYPE>purchase</TRANSACTION_TYPE>
<VERIFICATION_METHOD>unknown</VERIFICATION_METHOD>
<DUPLICATE>false</DUPLICATE>
</RECEIPT>]]>
</RECEIPT>
</INTERACTION>
</POI_MSG>
CDATA is an encoding mechanism to include arbitrary data within an XML file. Everything within CDATA is parsed as a single string when loading the XML into a Document instance. If you need to access the contents of the CDATA as a DOM document, you will need to instantiate a second Document object from the string contents, make your changes, then serialize that back to a string and put the string back into a CDATA in the original document.
I dont think CDATA section will be parsed as other regular elements in the XML. CDATA section is purely to escape any syntax checks. My suggestion would be use a element to represent the data in CDATA section. If you still want to use CDATA section, I guess you'll need parse the section as a string and then load the data into a Document.
I'm using javax.xml.transform.Transformer class to transform the DOM source into XML string. I have some empty elements in DOM tree, and these become one tag which I don't want.
How do I prevent <sampletag></sampletag> from becoming <sampletag/>?
I hade the same problem.
This is the function to get that result.
public static String fixClosedTag(String rawXml){
LinkedList<String[]> listTags = new LinkedList<String[]>();
String splittato[] = rawXml.split("<");
String prettyXML="";
int counter = 0;
for(int x=0;x<splittato.length;x++){
String tmpStr = splittato[x];
int indexEnd = tmpStr.indexOf("/>");
if(indexEnd>-1){
String nameTag = tmpStr.substring(0, (indexEnd));
String oldTag = "<"+ nameTag +"/>";
String newTag = "<"+ nameTag +"></"+ nameTag +">";
String tag[]=new String [2];
tag[0] = oldTag;
tag[1] = newTag;
listTags.add(tag);
}
}
prettyXML = rawXml;
for(int y=0;y<listTags.size();y++){
String el[] = listTags.get(y);
prettyXML = prettyXML.replaceAll(el[0],el[1]);
}
return prettyXML;
}
If you want to control how XML is formatted, provide your own ContentHandler to prettify XML into "text". It should not matter to the receiving end (unless human) whether it receives <name></name> or <name/> - they both mean the same thing.
The two representations are equivalent to an XML parser, so it doesn't matter.
If you want to process XML with anything else than an XML-parser, you will end up with a lot of work and an XML-parser anyway.
If the process you are sending it through NEEDS the element not to be self-closing (which it should not), you can force the element not to be self-closing by placing content inside of it.
How does the PDF converter handle XML comments or processing instructions?
<sampletag>!<--Sample Comment--></sampletag>
<sampletag><?SampleProcessingInstruction?></sampletag>
I tried below to prevent transform empty tags into single tag :
Transformer tf = TransformerFactory.newInstance().newTransformer();
tf.setOutputProperty(OutputKeys.METHOD,"html")
It's retaining empty tags.