简体   繁体   中英

JAXB Marshalling - Namespace for root element & element Ref name issue

I have couple simple questions regarding JAXB marshaling. I am trying to marshal a class containing the following fields:

@XmlElementRef(name = "AlternateVerificationKeys", namespace = "http://schemas.microsoft.com/Azure/MediaServices/KeyDelivery/TokenRestrictionTemplate/v1", type = JAXBElement.class, required = false)
protected JAXBElement<ArrayOfTokenVerificationKey> alternateVerificationKeys;

@XmlElement(name = "Audience", required = true, nillable = true)
@XmlSchemaType(name = "anyURI")
protected String audience;

@XmlElement(name = "Issuer", required = true, nillable = true)
@XmlSchemaType(name = "anyURI")
protected String issuer;

@XmlElement(name = "PrimaryVerificationKey", required = true, nillable = true)
protected TokenVerificationKey primaryVerificationKey;

@XmlElementRef(name = "RequiredClaims", namespace = "http://schemas.microsoft.com/Azure/MediaServices/KeyDelivery/TokenRestrictionTemplate/v1", type = JAXBElement.class, required = false)
protected JAXBElement<ArrayOfTokenClaim> requiredClaims;

@XmlElement(name = "TokenType", required = true)
@XmlSchemaType(name = "string")
protected TokenType tokenType;

And using simply the following serialization code:

public static String asString(JAXBContext pContext, Object pObject) throws JAXBException {
    StringWriter sw = new StringWriter();

    Marshaller marshaller = pContext.createMarshaller();
    marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
    marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
    marshaller.marshal(pObject, sw);

    return sw.toString();
}

The output I get is:

<TokenRestrictionTemplate xmlns="http://schemas.microsoft.com/Azure/MediaServices/KeyDelivery/TokenRestrictionTemplate/v1">
    <ArrayOfTokenVerificationKey>
        <TokenVerificationKey
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="SymmetricVerificationKey">
            <KeyValue></KeyValue>
        </TokenVerificationKey>
    </ArrayOfTokenVerificationKey>
    <Audience>urn:test</Audience>
    <Issuer>http://testacs.com/</Issuer>
    <PrimaryVerificationKey
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="SymmetricVerificationKey">
        <KeyValue></KeyValue>
    </PrimaryVerificationKey>
    <ArrayOfTokenClaim>
        <TokenClaim>
            <ClaimType>urn:microsoft:azure:mediaservices:contentkeyidentifier</ClaimType>
            <ClaimValue
                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
        </TokenClaim>
    </ArrayOfTokenClaim>
    <TokenType>SWT</TokenType>
</TokenRestrictionTemplate>

Now, here are the problems I am facing:

  1. I want the namespace xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" to appear in the root element ie in TokenRestrictionTemplate instead of individual child elements. How can I achieve that?

  2. I have some elements eg JAXBElement with @XmlElementRef(name = "AlternateVerificationKeys" ...) but when I marshal the name of the child element appears to be ArrayOfTokenVerificationKey rather than AlternateVerificationKeys . How can I fix this?

I want the namespace xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" to appear in the root element ie in TokenRestrictionTemplate instead of individual child elements. How can I achieve that?

That is a core feature of XML. You can achieve this by using an abstract class and multiple concrete classes inherited from first one. Also you should annotate the abstract class with @XmlTransient and @XmlSeeAlso who receive a list of concrete classes. An example below.

1. Create an abstract class named TokenVerificationKey , with these annotations.

@XmlTransient
@XmlSeeAlso({SymmetricVerificationKey.class, X509CertTokenVerificationKey.class})
public abstract class TokenVerificationKey {

}

2. Finnaly, define the concrete classes with these annotations:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "SymmetricVerificationKey")
public class SymmetricVerificationKey extends TokenVerificationKey {

    @XmlElement(name = "KeyValue")
    private byte[] keyValue;

}

I have some elements eg JAXBElement with @XmlElementRef(name = "AlternateVerificationKeys" ...) but when I marshal the name of the child element appears to be ArrayOfTokenVerificationKey rather than AlternateVerificationKeys. How can I fix this?

You should annotate TokenRestrictionTemplate.alternateVerificationKeys using @XmlElementWrapper to create the collection (a wrapper) element and @XmlElement to set the childs element name:

@XmlElementWrapper(name = "AlternateVerificationKeys")
@XmlElement(name = "TokenVerificationKey")
private List<TokenVerificationKey> alternateVerificationKeys;

This setup combined with the previous one, makes an output as below.

With this technique you will get this output:

<TokenRestrictionTemplate xmlns="http://schemas.microsoft.com/Azure/MediaServices/KeyDelivery/TokenRestrictionTemplate/v1" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
    <AlternateVerificationKeys>
        <TokenVerificationKey i:type="SymmetricVerificationKey">
            <KeyValue>cGVwZXBlZGtkZGllZGpkamRqZGplaWRqZWVpZGplaWRqaWpkZWltZGVpbWRlaWRpZWQ=</KeyValue>
        </TokenVerificationKey>
        <TokenVerificationKey i:type="X509CertTokenVerificationKey">
            <RawBody>MIIDqDCCAxGgAwIBAgIJAMAU1MJasjgBMA0GCSqGSIb3DQEBBQUAMIGVMQswCQYDVQQGEwJBUjENMAsGA1UECBMEQlNBUzENMAsGA1UEBxMEQ0FCQTEXMBUGA1UEChMOU291dGh3b3JrcyBTUkwxGTAXBgNVBAsTEERldmVsb3BtZW50IFVuaXQxCzAJBgNVBAMTAkVWMScwJQYJKoZIhvcNAQkBFhh2ZWNjaGlvZW1hbnVlbEBnbWFpbC5jb20wHhcNMTUwNzE0MTc1MjQ0WhcNMTgwNDEwMTc1MjQ0WjCBlTELMAkGA1UEBhMCQVIxDTALBgNVBAgTBEJTQVMxDTALBgNVBAcTBENBQkExFzAVBgNVBAoTDlNvdXRod29ya3MgU1JMMRkwFwYDVQQLExBEZXZlbG9wbWVudCBVbml0MQswCQYDVQQDEwJFVjEnMCUGCSqGSIb3DQEJARYYdmVjY2hpb2VtYW51ZWxAZ21haWwuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDJC2kNjNnBw2hyyI3Hbu30PRXw17GGfHytyvgB89nuakoFE2Oe7Ic8NDPN5/WeA7MROH7dDKole+MFOjDeSO/8d7ylWieVu08/yVpJwcgM3hV7X9Jbc5Zl0XsMXUxXaLvYAaLb3Kc8LrPPBJtTI2bXesV7AGxYKqcDIpwfQ7BGHwIDAQABo4H9MIH6MB0GA1UdDgQWBBTKcw3CsHdNrjqsOPKCoD+59Nh9ijCBygYDVR0jBIHCMIG/gBTKcw3CsHdNrjqsOPKCoD+59Nh9iqGBm6SBmDCBlTELMAkGA1UEBhMCQVIxDTALBgNVBAgTBEJTQVMxDTALBgNVBAcTBENBQkExFzAVBgNVBAoTDlNvdXRod29ya3MgU1JMMRkwFwYDVQQLExBEZXZlbG9wbWVudCBVbml0MQswCQYDVQQDEwJFVjEnMCUGCSqGSIb3DQEJARYYdmVjY2hpb2VtYW51ZWxAZ21haWwuY29tggkAwBTUwlqyOAEwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQBVuuFO3cyOPKSE1nvNyshAeRqhRD+igICeCHGP2zpFQ05k4lzRoio/+1/zdkwZ7g1jHSXz+QkGPRjAxMKGoK8huiTvJwJ1jDVNqyJqfnCW4S2AoNCwziKgGriL7luSDZ+PG3MxoNaCB63B6M6OKOezXhFk4VeLJ/NY1Eohe9E1ew==</RawBody>
        </TokenVerificationKey>
    </AlternateVerificationKeys>
    <Audience>http://audience.com</Audience>
    <Issuer>https://issuer.com</Issuer>
    <PrimaryVerificationKey i:type="SymmetricVerificationKey">
        <KeyValue>cGVwZXBlZGtkZGllZGpkamRqZGplaWRqZWVpZGplaWRqaWpkZWltZGVpbWRlaWRpZWQ=</KeyValue>
    </PrimaryVerificationKey>
    <RequiredClaims>
        <TokenClaim>
            <ClaimType>urn:microsoft:azure:mediaservices:contentkeyidentifier</ClaimType>
            <ClaimValue i:nil="true"/>
        </TokenClaim>
    </RequiredClaims>
    <TokenType>SWT</TokenType>
</TokenRestrictionTemplate> 

Aditional Info:

You could want define the namespace once at the beginning of the XML. You should define the "com.sun.xml.bind.namespacePrefixMapper" propery. You can also set the preferred prefix for the namespace.

btw: Azure SDK for .NET, uses "i" as prefix.

    Marshaller m = context.createMarshaller();
    m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
    m.setProperty("com.sun.xml.bind.namespacePrefixMapper", new    NamespacePrefixMapper() {
        @Override
        public String[] getPreDeclaredNamespaceUris() {
            return new String[] {
                    // "http://www.w3.org/2001/XMLSchema-instance"
                    XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI
                    };
        }

        @Override
        public String getPreferredPrefix(String namespaceUri, String suggestion, boolean requirePrefix) {
            // "http://www.w3.org/2001/XMLSchema-instance"
            if (namespaceUri.equals(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI)) {
                return "i";
            }
            return suggestion;
        }
    });

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM