I found this question on SO. I shows how to get the line numbers of individual xml elements while unmarshalling with JAXB. I extended this example being able to use xinclude. Unfortunately, I'm not able to get the line number of the xinclude.
So, how do I get the line number of the actual xinclude statement? Here is the extended example:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.sax.SAXSource;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
public class Demo {
public static void main(String[] args) throws JAXBException, SAXException,
ParserConfigurationException, FileNotFoundException {
JAXBContext jc = JAXBContext.newInstance(Person.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
SAXParserFactory spf = SAXParserFactory.newInstance();
spf.setXIncludeAware(true);
spf.setNamespaceAware(true);
spf.setValidating(true);
File file = new File("src/person.xml");
XMLReader xr = spf.newSAXParser().getXMLReader();
xr.setEntityResolver(new EntityResolver() {
#Override
public InputSource resolveEntity(String publicId, String systemId)
throws SAXException, IOException {
System.out.println(publicId + " -> " + systemId);
return null;
}
});
SAXSource source = new SAXSource(xr, new InputSource(
new FileInputStream(file)));
Person person = (Person) unmarshaller.unmarshal(source);
System.out.println("Person: " + person.locator.getLineNumber());
System.out.println("Address: "
+ person.address.locator.getLineNumber());
}
}
The EntityResolver listener tells me that there is an xinclude statement, but I don't know on which line number.
person.xml
<?xml version="1.0" encoding="UTF-8"?>
<person xmlns:xi="http://www.w3.org/2001/XInclude">
<name>Jane Doe</name>
<xi:include href="./src/address.xml" />
</person>
address.xml
<?xml version="1.0" encoding="UTF-8"?>
<address>1 A Street</address>
I didn't mention the Person and Address class, so this questions stays short and compact :) I also marked every Locator field with #XmlTransient. Thank you!
Related
The above screen capture shows the expected output and actual output, red color indicates that it differs from the actual output that is shown in green color.
To create xml document I have used the marshall concept.Java code used to create xml document are given below.
import com.ehf.bean.Invoice;
import com.sap._0050089212_one_off.ypt74nkey_.StandardFaultMessage;
import java.io.FileOutputStream;
import java.io.IOException;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import org.xml.sax.SAXException;
public class Ehf {
public static void main(String[] args) throws ParserConfigurationException,
TransformerException, SAXException, IOException, StandardFaultMessage,
com.sap.xi.a1s.global.StandardFaultMessage, JAXBException {
JAXBContext contextObj = JAXBContext.newInstance(Invoice.class);
Marshaller marshallerObj = contextObj.createMarshaller();
marshallerObj.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
Invoice invoice = new Invoice();
invoice.setCustomizationID("dsf");
invoice.setInvoiceTypeCode(0);
marshallerObj.marshal(invoice, new FileOutputStream("question.xml"));
}
}
Note: Invoice class is generated using xsd, through xjc command.
How can resolve this problem?
#javax.xml.bind.annotation.XmlSchema(
namespace = "urn:oasis:names:specification:ubl:schema:xsd:Invoice-2",
elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED,
xmlns ={#XmlNs(prefix="cac", namespaceURI="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2"),
#XmlNs(prefix="cbc", namespaceURI="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2"),
#XmlNs(prefix="", namespaceURI="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2")
})
The above code is working for me as expected, this code should be write in package-info.java
Update
I have figured out why the reference validation is failing. Unfortunately, I can't figure out what to do about it.
When parsing the "sigString" String variable into the "doc" Document variable, the LINEFEED characters are being converted into SPACE characters. I have confirmed this by doing a manual generation of the digest value and comparing it to the value that is computed by the Java code.
Does anybody know how to preserve the LINEFEED characters when parsing an XML string into a org.w3c.dom.Document variable?
Original Post
I am attempting to write some java code to verify the XML digital signature of a SAML response. I have verified the SAML response with other tools, so I know it is valid (excluding timing issues, not a factor to the digital signature). Below is the code I have used that I believe should be able to do this validation as well as the signature I am trying to validate.
When I run the code, I get the following output
Signature 0:
..Signature failed core validation
....signature validation status: true
....ref[0, #id14167335278088961501144300] validation status: false
Signature 1:
..Signature passed core validation
....signature validation status: true
....ref[0, #id141673352781342501524143644] validation status: true
I have no idea why the digest for reference id14167335278088961501144300 is not validating. Can anyone shed some light on what I am doing wrong?
Note: I am loading the XSD from a URL in this example so I don't have to include 4 XSD files in my question. However, because of this, the program can take a minute to run. I know this slowdown can be eliminated with local XSD files, it just is not feasible to do that way with the posted code
XMLDSigVerifier.java:
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Iterator;
import javax.xml.XMLConstants;
import javax.xml.crypto.MarshalException;
import javax.xml.crypto.dsig.Reference;
import javax.xml.crypto.dsig.XMLSignature;
import javax.xml.crypto.dsig.XMLSignatureException;
import javax.xml.crypto.dsig.XMLSignatureFactory;
import javax.xml.crypto.dsig.dom.DOMValidateContext;
import javax.xml.crypto.test.dsig.X509KeySelector;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
public class XMLDSigVerifier {
public static void main (String[] args) throws ParserConfigurationException, SAXException, IOException, KeyStoreException, MarshalException, XMLSignatureException, NoSuchAlgorithmException, CertificateException {
//Get XML as a string, will be parameter in final version
String sigString = new String(Files.readAllBytes(Paths.get("src/signature.xml")), StandardCharsets.UTF_8);
InputSource is = new InputSource();
is.setCharacterStream(new StringReader(sigString));
//Get X509Certificate as a string, will be parameter in final version
String samlCertString= "-----BEGIN CERTIFICATE-----MIIDpDCCAoygAwIBAgIGAVjUwdcaMA0GCSqGSIb3DQEBBQUAMIGSMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsGA1UECgwET2t0YTEUMBIGA1UECwwLU1NPUHJvdmlkZXIxEzARBgNVBAMMCmRldi02NjI2ODExHDAaBgkqhkiG9w0BCQEWDWluZm9Ab2t0YS5jb20wHhcNMTYxMjA2MTUyOTIzWhcNMjYxMjA2MTUzMDIzWjCBkjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xDTALBgNVBAoMBE9rdGExFDASBgNVBAsMC1NTT1Byb3ZpZGVyMRMwEQYDVQQDDApkZXYtNjYyNjgxMRwwGgYJKoZIhvcNAQkBFg1pbmZvQG9rdGEuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvLvIOJ11216IzuqZAbTiAoJy6WYxWuGXeUn4rpYkXLGOO0OoDahzhyquCQgLQC8mGlxCAq8gETQcdL+SX7lOlavHcNYiaYUD9IipMV0Kqt8TgfLO8UuYLb2jNNaQp+0tbcYv4SHpC4nXTndlo2nk3cJVELvXYfvjqKzDvtMwACy37Vc01GZbFQXhSEfBt9J2aQzLPFzH/RxKeOjzKW0kxWgYpfP0NZPtwkHrsdZaqpaR+039v5bckVSQvs0ZMz1Ionv+keWzM6YpQg7sF/OsvN05u0tkrGYDq1BoM9yH1h11bXrVhLvdOjo4bSVjeiAZ2LNOTurGO4JfH+CqT15c5wIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQC8jLBoAM35/6pwtUJ86BhYKvtjB6t3k+5uFRUx8rBWYL5atirRPF73W4f6AIkIp36zkS0os0RHuFK0bG2FPnjQj+FpErd8zji8PVFQ2LZ0WPNLYP7g7BWxAoNct2q0Iw3TACY6h722Cq6WS9ZP4O2iv3kkpo4A7JZvuf4yGGY2nVfx5nLZAmcEA9bZmHhcgmPLs2FBYpLYPs/5P4nd2HeiTJW+F6M75g9E4wG+sf3q2zqzh+AmV5kHffWnGx2MPdUmyFPU80zcDzEpodVU73YUxJIJScwjjXzytuQSB2FcM0TMqwYUq2qGVAPhBw4nnAxeScbMyMbFVDrNyMeejDhq-----END CERTIFICATE-----";
X509Certificate samlCert;
samlCert = parseCertificate(samlCertString);
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(null, null);
ks.setCertificateEntry("a", samlCert);
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
// File xsd = new File("src/saml-schema-protocol-2.0.xsd");
// Schema schema = schemaFactory.newSchema(xsd);
Schema schema = schemaFactory.newSchema(new URL("http://docs.oasis-open.org/security/saml/v2.0/saml-schema-protocol-2.0.xsd"));
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
dbf.setSchema(schema);
DocumentBuilder builder = dbf.newDocumentBuilder();
Document doc = builder.parse(is);
NodeList signatureNodeList = doc.getElementsByTagNameNS(XMLSignature.XMLNS, "Signature");
Node signatureNode;
for (int sigIndex = 0; sigIndex < signatureNodeList.getLength(); sigIndex++) {
signatureNode = signatureNodeList.item(sigIndex);
if (sigIndex > 0) {
System.out.println("");
}
System.out.println("Signature " + sigIndex + ":");
DOMValidateContext valContext = new DOMValidateContext(new X509KeySelector(ks), signatureNode);
valContext.setProperty("javax.xml.crypto.dsig.cacheReference", Boolean.TRUE);
XMLSignatureFactory factory = XMLSignatureFactory.getInstance("DOM");
XMLSignature signature = factory.unmarshalXMLSignature(valContext);
boolean coreValidity = signature.validate(valContext);
//Check Validity
if (coreValidity == false) {
System.err.println("..Signature failed core validation");
try {
//Sleep because of eclipse bug
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
System.out.println("..Signature passed core validation");
}
//Check validity Parts
//Validity Part 1: Check Signature Validation
boolean sv = signature.getSignatureValue().validate(valContext);
System.out.println("....signature validation status: " + sv);
//Validity Part 2: Check References
Iterator<?> i = signature.getSignedInfo().getReferences().iterator();
for (int j = 0; i.hasNext(); j++) {
Reference ref = (Reference) i.next();
boolean refValid = ref.validate(valContext);
System.out.println("....ref[" + j + ", " + ref.getURI() + "] validation status: " + refValid);
}
}
}
public static X509Certificate parseCertificate(String certStr) throws CertificateException{
java.util.Base64.Decoder dec = java.util.Base64.getDecoder();
String beginCert = "-----BEGIN CERTIFICATE-----";
String endCert = "-----END CERTIFICATE-----";
byte [] decoded = dec.decode(
certStr
.replaceAll(beginCert, "")
.replaceAll(endCert, "")
);
return (X509Certificate)CertificateFactory.getInstance("X.509").generateCertificate(new ByteArrayInputStream(decoded));
}
}
signature.xml
<?xml version="1.0" encoding="UTF-8"?><saml2p:Response xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol" Destination="https://apps.surdellpartners.com/ords/kohls_wmj/workamajig/okta/acs/" ID="id14167335278088961501144300" IssueInstant="2017-01-27T18:21:53.483Z" Version="2.0" xmlns:xs="http://www.w3.org/2001/XMLSchema"><saml2:Issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">http://www.okta.com/exk8y9z9v7FSYL34Y0h7</saml2:Issuer><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/><ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/><ds:Reference URI="#id14167335278088961501144300"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"><ec:InclusiveNamespaces xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="xs"/></ds:Transform></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><ds:DigestValue>7HyoOBjYlu8fbvSMNIY4O0fc6BhrkAUaPrF9EYWq/wE=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>dOhPiIwGvLJac40XW4x5Xn5soIzImitr/HAxRojDwSbAbfRp9t/VuRFT2Rat5oGgV3tWHedN7VBNpSGzBfZsYMBB/s/WYH0EQisTuS8iGXpNzxUIb0e9zbSFZIDiy3M1Zi8afC/EPrbP/SlBnXR0UXuyJW8KbfMZq9suw2GghVLI03q8FjLdFGBb4VZ2X5hqr4+qSpnS1+8RB+0bqtADKjYt9DP7vuhxz1jBI5o29OQOFZjW2K2g9Qr7ANUzaNFnIW50Lo4BH/qt60tA/UuZUNxwY+z3MM5ARb4zQYa8O/yQOPfvcKb0Ff9V4hHjlNtiZoZCOWq/2+ir9t7wxPwvNg==</ds:SignatureValue><ds:KeyInfo><ds:X509Data><ds:X509Certificate>MIIDpDCCAoygAwIBAgIGAVjUwdcaMA0GCSqGSIb3DQEBBQUAMIGSMQswCQYDVQQGEwJVUzETMBEG
A1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsGA1UECgwET2t0YTEU
MBIGA1UECwwLU1NPUHJvdmlkZXIxEzARBgNVBAMMCmRldi02NjI2ODExHDAaBgkqhkiG9w0BCQEW
DWluZm9Ab2t0YS5jb20wHhcNMTYxMjA2MTUyOTIzWhcNMjYxMjA2MTUzMDIzWjCBkjELMAkGA1UE
BhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xDTALBgNV
BAoMBE9rdGExFDASBgNVBAsMC1NTT1Byb3ZpZGVyMRMwEQYDVQQDDApkZXYtNjYyNjgxMRwwGgYJ
KoZIhvcNAQkBFg1pbmZvQG9rdGEuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
vLvIOJ11216IzuqZAbTiAoJy6WYxWuGXeUn4rpYkXLGOO0OoDahzhyquCQgLQC8mGlxCAq8gETQc
dL+SX7lOlavHcNYiaYUD9IipMV0Kqt8TgfLO8UuYLb2jNNaQp+0tbcYv4SHpC4nXTndlo2nk3cJV
ELvXYfvjqKzDvtMwACy37Vc01GZbFQXhSEfBt9J2aQzLPFzH/RxKeOjzKW0kxWgYpfP0NZPtwkHr
sdZaqpaR+039v5bckVSQvs0ZMz1Ionv+keWzM6YpQg7sF/OsvN05u0tkrGYDq1BoM9yH1h11bXrV
hLvdOjo4bSVjeiAZ2LNOTurGO4JfH+CqT15c5wIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQC8jLBo
AM35/6pwtUJ86BhYKvtjB6t3k+5uFRUx8rBWYL5atirRPF73W4f6AIkIp36zkS0os0RHuFK0bG2F
PnjQj+FpErd8zji8PVFQ2LZ0WPNLYP7g7BWxAoNct2q0Iw3TACY6h722Cq6WS9ZP4O2iv3kkpo4A
7JZvuf4yGGY2nVfx5nLZAmcEA9bZmHhcgmPLs2FBYpLYPs/5P4nd2HeiTJW+F6M75g9E4wG+sf3q
2zqzh+AmV5kHffWnGx2MPdUmyFPU80zcDzEpodVU73YUxJIJScwjjXzytuQSB2FcM0TMqwYUq2qG
VAPhBw4nnAxeScbMyMbFVDrNyMeejDhq</ds:X509Certificate></ds:X509Data></ds:KeyInfo></ds:Signature><saml2p:Status xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol"><saml2p:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/></saml2p:Status><saml2:Assertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" ID="id141673352781342501524143644" IssueInstant="2017-01-27T18:21:53.483Z" Version="2.0" xmlns:xs="http://www.w3.org/2001/XMLSchema"><saml2:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity" xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">http://www.okta.com/exk8y9z9v7FSYL34Y0h7</saml2:Issuer><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/><ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/><ds:Reference URI="#id141673352781342501524143644"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"><ec:InclusiveNamespaces xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="xs"/></ds:Transform></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><ds:DigestValue>EhCfdS3vR7BT/a3ITrTVoSazAvQbiKlAeMpaOJv9wEs=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>GSbmKitI52o4eZm94ivVGfXpLHiiTCvvvy6uEfO+37Z95kK8hU3OniWeZW01NlqxrDqC1eZkrPBEqMWPW0+K5oeiHkaedCpVafn1mZLNYQNKJetKhYNczK7tiCZn9P66JOXfPfALOlsC0nVvL10m5CmLjQ/m1VW8BE5N58OsNO8mCDycupvFMp/Q0tvvAf5DUkY2A3Y6Chx/i4cAYpJKGjBppNNpdzVYNlvKYvImk6d25Gx+1J7H7+0uYheYkEPFLd+7Kr8rUpqV2t33iEyILc3LXabQmmNiNHxaGD9gmccsGNTddm3Q1Uso2SXm2lw6/FqdozbKFxs6qJKrm9VmLQ==</ds:SignatureValue><ds:KeyInfo><ds:X509Data><ds:X509Certificate>MIIDpDCCAoygAwIBAgIGAVjUwdcaMA0GCSqGSIb3DQEBBQUAMIGSMQswCQYDVQQGEwJVUzETMBEG
A1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsGA1UECgwET2t0YTEU
MBIGA1UECwwLU1NPUHJvdmlkZXIxEzARBgNVBAMMCmRldi02NjI2ODExHDAaBgkqhkiG9w0BCQEW
DWluZm9Ab2t0YS5jb20wHhcNMTYxMjA2MTUyOTIzWhcNMjYxMjA2MTUzMDIzWjCBkjELMAkGA1UE
BhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xDTALBgNV
BAoMBE9rdGExFDASBgNVBAsMC1NTT1Byb3ZpZGVyMRMwEQYDVQQDDApkZXYtNjYyNjgxMRwwGgYJ
KoZIhvcNAQkBFg1pbmZvQG9rdGEuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
vLvIOJ11216IzuqZAbTiAoJy6WYxWuGXeUn4rpYkXLGOO0OoDahzhyquCQgLQC8mGlxCAq8gETQc
dL+SX7lOlavHcNYiaYUD9IipMV0Kqt8TgfLO8UuYLb2jNNaQp+0tbcYv4SHpC4nXTndlo2nk3cJV
ELvXYfvjqKzDvtMwACy37Vc01GZbFQXhSEfBt9J2aQzLPFzH/RxKeOjzKW0kxWgYpfP0NZPtwkHr
sdZaqpaR+039v5bckVSQvs0ZMz1Ionv+keWzM6YpQg7sF/OsvN05u0tkrGYDq1BoM9yH1h11bXrV
hLvdOjo4bSVjeiAZ2LNOTurGO4JfH+CqT15c5wIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQC8jLBo
AM35/6pwtUJ86BhYKvtjB6t3k+5uFRUx8rBWYL5atirRPF73W4f6AIkIp36zkS0os0RHuFK0bG2F
PnjQj+FpErd8zji8PVFQ2LZ0WPNLYP7g7BWxAoNct2q0Iw3TACY6h722Cq6WS9ZP4O2iv3kkpo4A
7JZvuf4yGGY2nVfx5nLZAmcEA9bZmHhcgmPLs2FBYpLYPs/5P4nd2HeiTJW+F6M75g9E4wG+sf3q
2zqzh+AmV5kHffWnGx2MPdUmyFPU80zcDzEpodVU73YUxJIJScwjjXzytuQSB2FcM0TMqwYUq2qG
VAPhBw4nnAxeScbMyMbFVDrNyMeejDhq</ds:X509Certificate></ds:X509Data></ds:KeyInfo></ds:Signature><saml2:Subject xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion"><saml2:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">jason.lyle88#gmail.com</saml2:NameID><saml2:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"><saml2:SubjectConfirmationData NotOnOrAfter="2017-01-27T18:26:53.483Z" Recipient="https://apps.surdellpartners.com/ords/kohls_wmj/workamajig/okta/acs/"/></saml2:SubjectConfirmation></saml2:Subject><saml2:Conditions NotBefore="2017-01-27T18:16:53.483Z" NotOnOrAfter="2017-01-27T18:26:53.483Z" xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion"><saml2:AudienceRestriction><saml2:Audience>http://localhost:8081/spring-security-saml2-sample/saml/metadata</saml2:Audience></saml2:AudienceRestriction></saml2:Conditions><saml2:AuthnStatement AuthnInstant="2017-01-27T18:21:53.483Z" SessionIndex="id1485541313483.890561695" xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion"><saml2:AuthnContext><saml2:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml2:AuthnContextClassRef></saml2:AuthnContext></saml2:AuthnStatement><saml2:AttributeStatement xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion"><saml2:Attribute Name="firstName" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic"><saml2:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">Jason</saml2:AttributeValue></saml2:Attribute><saml2:Attribute Name="lastName" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic"><saml2:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">Lyle</saml2:AttributeValue></saml2:Attribute></saml2:AttributeStatement></saml2:Assertion></saml2p:Response>
I am using XMLStreamReader to achieve my goal(splitting xml file). It looks good, but still does not give the desired result. My aim is to split every node "nextTag" from an input file:
<?xml version="1.0" encoding="UTF-8"?>
<firstTag>
<nextTag>1</nextTag>
<nextTag>2</nextTag>
</firstTag>
The outcome should look like this:
<?xml version="1.0" encoding="UTF-8"?><nextTag>1</nextTag>
<?xml version="1.0" encoding="UTF-8"?><nextTag>2</nextTag>
Referring to Split 1GB Xml file using Java I achieved my goal with this code:
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringWriter;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stax.StAXSource;
import javax.xml.transform.stream.StreamResult;
public class Demo4 {
public static void main(String[] args) throws Exception {
InputStream inputStream = new FileInputStream("input.xml");
BufferedReader in = new BufferedReader(new InputStreamReader(inputStream));
XMLInputFactory factory = XMLInputFactory.newInstance();
TransformerFactory tf = TransformerFactory.newInstance();
Transformer t = tf.newTransformer();
XMLStreamReader streamReader = factory.createXMLStreamReader(in);
while (streamReader.hasNext()) {
streamReader.next();
if (streamReader.getEventType() == XMLStreamReader.START_ELEMENT
&& "nextTag".equals(streamReader.getLocalName())) {
StringWriter writer = new StringWriter();
t.transform(new StAXSource(streamReader), new StreamResult(
writer));
String output = writer.toString();
System.out.println(output);
}
}
}
}
Actually very simple. But, my input file is in form from a single line:
<?xml version="1.0" encoding="UTF-8"?><firstTag><nextTag>1</nextTag><nextTag>2</nextTag></firstTag>
My Java code does not produce the desired output anymore, instead just this result:
<?xml version="1.0" encoding="UTF-8"?><nextTag>1</nextTag>
After spending hours, I am pretty sure to already find out the reason:
t.transform(new StAXSource(streamReader), new StreamResult(writer));
It is because, after the transform method being executed, the cursor will automatically moved forward to the next event. And in the code, I have this fraction:
while (streamReader.hasNext()) {
streamReader.next();
...
t.transform(new StAXSource(streamReader), new StreamResult(writer));
...
}
After the first transform, the streamReader gets directly 2 times next():
1. from the transform method
2. from the next method in the while loop
So, in case of this specific line XML, the cursor can never achive the second open tag .
In opposite, if the input XML has a pretty print form, the second can be reached from the cursor because there is a space-event after the first closing tag
Unfortunately, I could not find anything how to do settings, so that the transformator does not automatically spring to next event after performing the transform method. This is so frustating.
Does anybody have any idea how I can deal with it? Also semantically is very welcome. Thank you so much.
Regards,
Ratna
PS. I can surely write a workaround for this problem(pretty print the xml document before transforming it, but this would mean that the input xml was being modified before, this is not allowed)
As you elaborated did the transformation step proceed to the next create element if the element-nodes follow directly each other.
In order to deal with this, you can rewrite you code using nested while loops, like this:
while(reader.next() != XMLStreamConstants.END_DOCUMENT) {
while(reader.getEventType() == XMLStreamConstants.START_ELEMENT && reader.getLocalName().equals("nextTag")) {
StringWriter writer = new StringWriter();
// will transform the current node to a String, moves the cursor to the next START_ELEMENT
t.transform(new StAXSource(reader), new StreamResult(writer));
System.out.println(writer.toString());
}
}
In case your xml file fits in memory, you can try with the help of the JOOX library, imported in gradle like:
compile 'org.jooq:joox:1.3.0'
And the main class, like:
import java.io.File;
import java.io.IOException;
import org.joox.JOOX;
import org.joox.Match;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import static org.joox.JOOX.$;
public class Main {
public static void main(String[] args)
throws IOException, SAXException, TransformerException {
DocumentBuilder builder = JOOX.builder();
Document document = builder.parse(new File(args[0]));
Transformer transformer =
TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty("omit-xml-declaration", "no");
final Match $m = $(document);
$m.find("nextTag").forEach(tag -> {
try {
transformer.transform(
new DOMSource(tag),
new StreamResult(System.out));
System.out.println();
}
catch (TransformerException e) {
System.exit(1);
}
});
}
}
It yields:
<?xml version="1.0" encoding="UTF-8"?><nextTag>1</nextTag>
<?xml version="1.0" encoding="UTF-8"?><nextTag>2</nextTag>
I have spent some time to investigate what is the problem but I couldn't solve it. When I unmarshal below XML and marshal back I see different XML.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
<one>test</one>
<three>\MySG\test.jsp</three>
<two>
<st>
<seeta>
<Source>
<problemtag xmlns="uuid:B89290D2-36FB-4EBC-A581-69B16D59EB92">
<p>deploy_test_page_renderingMetadata</p>
</problemtag>
</Source>
</seeta>
<Template id="tcm:1-63-32" title="Smart Compound Component Template"/>
<Publication id="tcm:0-1-1" title="Publication"/>
</st>
</two>
</root>
In the above xml only one tag (first one) expected remaining all (including namespace) are unexpected elements. Another application sends the above XML.
My Mapping are like this
package com.seeta.xml;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAnyElement;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name="root")
#XmlAccessorType(XmlAccessType.FIELD)
public class Root {
#XmlElement(name="one")
private String one;
public String getOne() {
return one;
}
public void setOne(String one) {
this.one = one;
}
#XmlElement(name="three")
private String three;
#XmlAnyElement
private List<Object> remaining = new ArrayList<Object>();
public String getThree() {
return three;
}
public void setThree(String three) {
this.three = three;
}
public List<Object> getRemaining() {
return remaining;
}
public void setRemaining(List<Object> remaining) {
this.remaining = remaining;
}
public String toString() {
return String.format("One [%s]-> Number of remaing elements [%d]-> three [%s]", one, remaining.size(), three);
}
}
Here is my simple code
package com.seeta.xml;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.net.URL;
import java.net.URLDecoder;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
public class JaxbSample {
public Document getDOMDocument(InputStream inputStream) throws ParserConfigurationException, SAXException, IOException {
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
documentBuilderFactory.setNamespaceAware(true);
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
if (inputStream != null) {
return documentBuilder.parse(new InputSource(inputStream));
} else {
return documentBuilder.newDocument();
}
}
public Root unmarshall(Document document) throws JAXBException {
JAXBContext context = JAXBContext.newInstance(Root.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
Root root = (Root) unmarshaller.unmarshal(document);
return root;
}
public Document marshall(Root root) throws JAXBException, ParserConfigurationException, SAXException, IOException {
JAXBContext context = JAXBContext.newInstance(Root.class);
Marshaller marshaller = context.createMarshaller();
Document document = getDOMDocument(null);
marshaller.marshal(root, document);
return document;
}
private String transform(Document document) throws TransformerException {
StringWriter sw = new StringWriter();
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
transformer.transform(new DOMSource(document), new StreamResult(sw));
return sw.toString();
}
public void testUnmarshallMarshallUsingDocument() throws ParserConfigurationException, SAXException, IOException, JAXBException, TransformerException {
InputStream inputStream = this.getClass().getResourceAsStream("jaxb.xml");
Document document = getDOMDocument(inputStream);
Root root = unmarshall(document);
Document documentAfterMarshal = marshall(root);
String output = transform(documentAfterMarshal);
System.out.println(output);
}
public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException, JAXBException, TransformerException {
JaxbSample jaxbTest = new JaxbSample();
jaxbTest.testUnmarshallMarshallUsingDocument();
}
}
output is
<root>
<one>test</one>
<three>\MySG\test.jsp</three>
<two>
<st>
<seeta>
<Source>
<problemtag:problemtag xmlns="uuid:B89290D2-36FB-4EBC-A581-69B16D59EB92" xmlns:problemtag="uuid:B89290D2-36FB-4EBC-A581-69B16D59EB92">
<p>deploy_test_page_renderingMetadata</p>
</problemtag:problemtag>
</Source>
</seeta>
<Template id="tcm:1-63-32" title="Smart Compound Component Template"/>
<Publication id="tcm:0-1-1" title="Publication"/>
</st>
</two>
</root>
And also I tried following
I tried with NamespacePrefixMapper. I can able to give different namespace but not empty(""). I don't want any namespace at all.
new NamespacePrefixMapper() {
public String getPreferredPrefix(String namespaceUri, String suggestion, boolean requirePrefix) {
return "";
}
};
We don't have any xsd ( at least I don't know) in our project for trying unqualified thing
I really didn't understand QName thing
If all you want is to preserve the unused elements and marshall them back, I think you ought to be able to do something like this:
#XmlRootElement(name="Root")
#XmlAccessorType(XmlAccessType.FIELD)
class Root {
#XmlElement(name="One")
private String one;
#XmlAnyElement
private List<Any> otherElements;
}
class AnyAdapter extends XmlAdapter<Element,Any> {
#Override
public Any unmarshal(Element element) throws Exception {
return new Any(element);
}
#Override
public Element marshal(Any any) throws Exception {
return any.element;
}
}
#XmlJavaTypeAdapter(AnyAdapter.class)
class Any {
Element element;
Any(Element element) {
this.element = element;
}
}
I don't want any namespace at all.
You won't be able to accomplish this with JAXB alone. The #XmlAnyElement tells the unmarshaller to dump the elements that it can't process into your list. Those elements have namespaces attached. When you then marshal those elements, they'll be written with their namespaces.
One option is for you to parse the incoming XML with a namespace-unaware DOM parser, then unmarshall it using the DOM tree. There's an example of this in the Unmarshaller JavaDoc (which uses a namespace-aware parser; it should be obvious what to change to make it namespace-unaware).
I really didn't understand QName thing
Do you mean that you don't understand why the output is a qualified name, or why it picked the particular prefix? Or what QNames mean?
It's a qualified name because that's the most unambiguous way to represent the element.
I can't tell you why it picked this particular prefix; the JAXP serializer picks short names like "ns1", "ns2", and so on.
Here I am not creating a RESTful service indeed I have to call an external Restful service from my java code. Currently I am implementing this using Apache HttpClient.
The response that I get from the web service is in XML format.
I need to extract the data from XML and put them on Java objects.
Rather than using SAX parser, I heard that we can use JAX-RS and JERSEY which automatically maps the xml tags to corresponding java objects.
I have being looking through but unable to find a source to get started.
I did look at existing links
Consuming RESTful APIs using Java
RESTful call in Java
Any help is appreciated in moving forward.
Thanks!!
UPDATE
as follow up with this: Can I do this way?? if the xml being returned
as 4
.....
If I am constructing a Person object, I believe this will choke up.
Can I just bind only the xml elements that I want? if Yes how can I do
that.
You could map this XML as follows:
input.xml
<?xml version="1.0" encoding="UTF-8"?>
<Persons>
<NumberOfPersons>2</NumberOfPersons>
<Person>
<Name>Jane</Name>
<Age>40</Age>
</Person>
<Person>
<Name>John</Name>
<Age>50</Age>
</Person>
</Persons>
Persons
package forum7177628;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name="Persons")
#XmlAccessorType(XmlAccessType.FIELD)
public class Persons {
#XmlElement(name="Person")
private List<Person> people;
}
Person
package forum7177628;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
#XmlAccessorType(XmlAccessType.FIELD)
public class Person {
#XmlElement(name="Name")
private String name;
#XmlElement(name="Age")
private int age;
}
Demo
package forum7177628;
import java.io.File;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Persons.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
Persons persons = (Persons) unmarshaller.unmarshal(new File("src/forum7177628/input.xml"));
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(persons, System.out);
}
}
Output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Persons>
<Person>
<Name>Jane</Name>
<Age>40</Age>
</Person>
<Person>
<Name>John</Name>
<Age>50</Age>
</Person>
</Persons>
ORIGINAL ANSWER
Below is an example of calling a RESTful service using the Java SE APIs including JAXB:
String uri =
"http://localhost:8080/CustomerService/rest/customers/1";
URL url = new URL(uri);
HttpURLConnection connection =
(HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setRequestProperty("Accept", "application/xml");
JAXBContext jc = JAXBContext.newInstance(Customer.class);
InputStream xml = connection.getInputStream();
Customer customer =
(Customer) jc.createUnmarshaller().unmarshal(xml);
connection.disconnect();
For More Information:
http://blog.bdoughan.com/2010/08/creating-restful-web-service-part-55.html
JAX-RS is the Java api for restful webservice. Jersey is an implementation from sun/oracle.
You need jaxb to convert your xml to a POJO. But it is not the always case that, converted object can be used without any transformation. If this is the scenario SAXParser is a nice solution.
Here is a nice tutorial on JAXB.
You could consider using jaxb to bind your java objects to an xml document (marshalling).
http://www.oracle.com/technetwork/articles/javase/index-140168.html#xmp1
I use Apache CXF to build my RESTful services, which is another JAX-RS implementation (it also provides a JAX-WS implementation). I also use its "org.apache.cxf.jaxrs.client.WebClient" class in unit tests, which will completely manage all the marshalling and unmarshalling under the covers. You give it a URL and ask for an object of a particular type, and it does all the work. I don't know if Jersey has similar facilities.
If you also need to convert that xml string that comes as a response to the service call, an x object you need can do it as follows:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.JAXB;
import javax.xml.bind.JAXBException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.CharacterData;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
public class RestServiceClient {
// http://localhost:8080/RESTfulExample/json/product/get
public static void main(String[] args) throws ParserConfigurationException,
SAXException {
try {
URL url = new URL(
"http://localhost:8080/CustomerDB/webresources/co.com.mazf.ciudad");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("Accept", "application/xml");
if (conn.getResponseCode() != 200) {
throw new RuntimeException("Failed : HTTP error code : "
+ conn.getResponseCode());
}
BufferedReader br = new BufferedReader(new InputStreamReader(
(conn.getInputStream())));
String output;
Ciudades ciudades = new Ciudades();
System.out.println("Output from Server .... \n");
while ((output = br.readLine()) != null) {
System.out.println("12132312");
System.err.println(output);
DocumentBuilder db = DocumentBuilderFactory.newInstance()
.newDocumentBuilder();
InputSource is = new InputSource();
is.setCharacterStream(new StringReader(output));
Document doc = db.parse(is);
NodeList nodes = ((org.w3c.dom.Document) doc)
.getElementsByTagName("ciudad");
for (int i = 0; i < nodes.getLength(); i++) {
Ciudad ciudad = new Ciudad();
Element element = (Element) nodes.item(i);
NodeList name = element.getElementsByTagName("idCiudad");
Element element2 = (Element) name.item(0);
ciudad.setIdCiudad(Integer
.valueOf(getCharacterDataFromElement(element2)));
NodeList title = element.getElementsByTagName("nomCiudad");
element2 = (Element) title.item(0);
ciudad.setNombre(getCharacterDataFromElement(element2));
ciudades.getPartnerAccount().add(ciudad);
}
}
for (Ciudad ciudad1 : ciudades.getPartnerAccount()) {
System.out.println(ciudad1.getIdCiudad());
System.out.println(ciudad1.getNombre());
}
conn.disconnect();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public static String getCharacterDataFromElement(Element e) {
Node child = e.getFirstChild();
if (child instanceof CharacterData) {
CharacterData cd = (CharacterData) child;
return cd.getData();
}
return "";
}
}
Note that the xml structure that I expected in the example was as follows:
<ciudad><idCiudad>1</idCiudad><nomCiudad>BOGOTA</nomCiudad></ciudad>