I am essentially trying to produce the following chunk of XML from JAXB Annotations.
<pCredentials xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:type="ns3:LoginCredentials">
<loginId>user</loginId>
<loginPassword>password</loginPassword>
<userType>super</userType>
</pCredentials>
I tried the following annotations and multiple variations of the same type:
#XmlElement(name = "pCredentials", namespace = "##default", type = com.foo.LoginCredentials.class)
private LoginCredentials pCredentials;
But it produces the following:
<pCredentials>
<loginId>user</loginId>
<loginPassword>password</loginPassword>
<userType>super</userType>
</pCredentials>
Any suggestions as to what type of annotations I can provide that will produce the type reference?
Thanks for the help...Jay
You JAXB implementation will only add an xsi:type attribute when the Java type of the value does not match the expected type of the element. This is why what you tried will not work (since you are saying the elements type is the same type as the value.
You could do the following:
#XmlElement(type = Object.class)
private LoginCredentials pCredentials;
Note
The xsi:type attribute will be required in the XML being unmarshalled.
You will need to include LoginCredentials in the list of classes given to bootstrap the JAXBContext.
For More Information
http://blog.bdoughan.com/2010/11/jaxb-and-inheritance-using-xsitype.html
Related
Consider class with type parameter, that should be used as Map key.
class Foo<K> {
var map: Map<K, Dynamic> = new Map();
}
This does not compile with error Type parameters of multi type abstracts must be known.
The reason is understandable - Map is abstract and it's underlying type is selected based on key type, so key type should be known when compiling new Map() expression. On the other hand non-generic type with type parameters are compiled once for all parameters.
Looks like adding #:generic metadata should help. But actually it does not. My guess was that this is because haxe compiler compiles #:generic types the same as it does with non-generic types, and only then does some extra work for generic type. So I was thinking that having a Map with key type defined by type parameter is impossible in haxe.
But recently I've stumbled upon this issue: https://github.com/HaxeFoundation/haxe/issues/2537
Simn's answer there says, that this can be done by adding #:remove #:generic metadata. And it actually works.
#:remove #:generic
class Foo<K> {
var map: Map<K, Dynamic> = new Map();
}
For me this looks like magic and I'm not comfortable with this. In documentation I only see that
#:remove Causes an interface to be removed from all implementing classes before generation. That does not explain why this works.
If you replace Map by haxe.ds.BalancedTree and use only #:generic without #:remove, you will see your class Foo generated on your target containing new BalancedTree< object, object > (object is Dynamic). That means, Foo class holds general BalancedTree< object, object > type, while Foo_String for example holds generic BalancedTree< string, object > type. Now, if you go back to your Map, you will see that "Abstract haxe.ds.Map has no #:to function that accepts haxe.IMap< Foo.K, Dynamic >", e.g. no Map< Dynamic, Dynamic > implementation exists. That's why you need to use #:remove which will actually stop generation of Foo class, or at least, remove new Map< Dynamic, Dynamic > from the Foo (#:remove "Causes an interface to be removed" is misleading here)
The following code
void testReference() {
List<String> source = new ArrayList<>()
source.add("element")
List reference = (ArrayList)source // all ok, creates reference as types match
assertSame(source,reference)
List copyNotReference = (LinkedList)source // should fail on GroovyCastException, creates copy instead
assertNotSame(source,copyNotReference) // this works, copy is a different object
copyNotReference.add("second element")
println source
println copyNotReference
}
only works in Groovy. In Java it fails on attempt to cast ArrayList to LinkedList.
In Groovy it creates a LinkedList instance, calling constructor
public LinkedList(Collection<? extends E> c)
and copying source data to the new instance.
The test outputs
[element]
[element, second element]
That behaviour only occurs when casting types that are subtypes of collections.
Question
What Groovy mechanism is responsible for this unexpected behaviour?
Groovy allows coercion of objects via casting them (asType). This is implemented for collections.
See the source
Converts the given collection to another type. A default concrete
type is used for List, Set, or SortedSet. If the given type has
a constructor taking a collection, that is used. Otherwise, the
call is deferred to {#link #asType(Object,Class)}. If this
collection is already of the given type, the same instance is
returned.
I am using JAXB javax.xml.bind.Marshaller to marshal XML and also doing XSD validation as a part of it.
My object to be marshal is of complex type like below
I have complex class like below
class A{
private B b;
private C c;
}
where B and C are other classes.
In case of XSD validation failure, I am getting proper error message in configured javax.xml.bind.ValidationEventHandler.
but the problem is, I am getting locator.object only either B or C where XSD validation fails.
[severity=FATAL_ERROR,message=cvc-maxLength-valid: Value 'test' with length = '4' is not facet-valid with respect to maxLength '1' for type '#AnonType_xxxxx'.,locator=[url=null,line=-1,column=-1,node=null,object=com.....C#1f4304be,field=null]]
Here, Is there any way that along with above message I can get always a full class object i.e. object=com.....A#1f4304be so that I can cast it and use for my futher business processing.
The challenge is Class B is the only class which contains unique identifier of users and hence whenever there is a failure for class C only, I am not able to find for which user, this failed as Class C does not have any unique information about user.
Seeking for advice and solution!
EDIT: Added a more complete example, which clarified the problem.
Some .NET attributes require a parameter of type Type. How does one declare these parameters in F#?
For example, in C# we can do this:
[XmlInclude(typeof(Car))]
[XmlInclude(typeof(Truck))]
class Vehicle { }
class Car : Vehicle { }
class Truck : Vehicle { }
But, in F# the following...
[<XmlInclude(typeof<Car>)>]
[<XmlInclude(typeof<Truck>)>]
type Vehicle() = class end
type Car() = inherit Vehicle()
type Truck() = inherit Car()
...results in a compiler error: This is not a constant expression or valid custom attribute value.
You should address a circular type dependency introduced by forward usage of types in attributes. The snippet below shows how this can be done in F#:
// Compiles OK
[<AttributeUsage(AttributeTargets.All, AllowMultiple=true)>]
type XmlInclude(t:System.Type) =
inherit System.Attribute()
[<XmlInclude(typeof<Car>)>]
[<XmlInclude(typeof<Truck>)>]
type Vehicle() = class end
and Car() = inherit Vehicle()
and Truck() = inherit Car()
Can you try putting together a more complete example that gives the error? I just quickly tried something similar and it works fine (in F# 3.0 in Visual Studio 2012):
type Car = C
type XmlInclude(typ:System.Type) =
inherit System.Attribute()
[<XmlInclude(typeof<Car>)>]
let foo = 0
I guess there is some tiny detail somewhere that confuses the F# compiler for some reason - but it should understand typeof (which is, in reality, a function) and allow its use in attributes.
Given the following example xsd snippet:
< xs:attribute name="SEGMENT" default="" use="optional" type="xs:string"/ >
when xjc generates the class containing the SEGMENT bean attribute, the following getter is auto-generated:
public String getSEGMENT() {
if (segment == null) {
return "";
} else {
return segment;
}
}
My question is how do you get it do the same for xs:element objects? In other words, given the following xsd snippet:
< xs:element name="NAME" default="" type="xs:string"/ >
I want to know if I can get xjc to generate the following:
public String getNAME() {
if (name == null) {
return "";
} else {
return name;
}
}
How can this be done?
JAXB doesn't generate the same code for an element with default value as it does for an attribute with default value because the XML schema differentiates between element and attribute defaults:
Default values of both attributes and elements are declared using the default attribute, although this attribute has a slightly different consequence in each case. When an attribute is declared with a default value, the value of the attribute is whatever value appears as the attribute's value in an instance document; if the attribute does not appear in the instance document, the schema processor provides the attribute with a value equal to that of the default attribute. Note that default values for attributes only make sense if the attributes themselves are optional, and so it is an error to specify both a default value and anything other than a value of optional for use.
The schema processor treats defaulted elements slightly differently. When an element is declared with a default value, the value of the element is whatever value appears as the element's content in the instance document; if the element appears without any content, the schema processor provides the element with a value equal to that of the default attribute. However, if the element does not appear in the instance document, the schema processor does not provide the element at all. In summary, the differences between element and attribute defaults can be stated as: Default attribute values apply when attributes are missing, and default element values apply when elements are empty.
You can always count on the default value for a missing attribute (from here the special getter) but there is a catch with a missing element value.
Nonetheless, when you unmarshall an instance, the unmarshaller knows how to handle the default value. See here for details:
Element default values and marshalling
Element default values and unmarshalling
XJC won't add the getter code or initialize the fields with the default value, so if you need the "null safe check" you can either add it yourself manually after the code is generated by XJC or try to use some plugin to do it automatically:
JAXB 2 Default Value Plugin
CXF XJC Default Value Plugin