Why won't .NET deserialize my primitive array from a web service? - java

Help! I have an Axis web service that is being consumed by a C# application. Everything works great, except that arrays of long values always come across as [0,0,0,0] - the right length, but the values aren't deserialized. I have tried with other primitives (ints, doubles) and the same thing happens. What do I do? I don't want to change the semantics of my service.

Here's what I ended up with. I have never found another solution out there for this, so if you have something better, by all means, contribute.
First, the long array definition in the wsdl:types area:
<xsd:complexType name="ArrayOf_xsd_long">
<xsd:complexContent mixed="false">
<xsd:restriction base="soapenc:Array">
<xsd:attribute wsdl:arrayType="soapenc:long[]" ref="soapenc:arrayType" />
</xsd:restriction>
</xsd:complexContent>
</xsd:complexType>
Next, we create a SoapExtensionAttribute that will perform the fix. It seems that the problem was that .NET wasn't following the multiref id to the element containing the double value. So, we process the array item, go find the value, and then insert it the value into the element:
[AttributeUsage(AttributeTargets.Method)]
public class LongArrayHelperAttribute : SoapExtensionAttribute
{
private int priority = 0;
public override Type ExtensionType
{
get { return typeof (LongArrayHelper); }
}
public override int Priority
{
get { return priority; }
set { priority = value; }
}
}
public class LongArrayHelper : SoapExtension
{
private static ILog log = LogManager.GetLogger(typeof (LongArrayHelper));
public override object GetInitializer(LogicalMethodInfo methodInfo, SoapExtensionAttribute attribute)
{
return null;
}
public override object GetInitializer(Type serviceType)
{
return null;
}
public override void Initialize(object initializer)
{
}
private Stream originalStream;
private Stream newStream;
public override void ProcessMessage(SoapMessage m)
{
switch (m.Stage)
{
case SoapMessageStage.AfterSerialize:
newStream.Position = 0; //need to reset stream
CopyStream(newStream, originalStream);
break;
case SoapMessageStage.BeforeDeserialize:
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = false;
settings.NewLineOnAttributes = false;
settings.NewLineHandling = NewLineHandling.None;
settings.NewLineChars = "";
XmlWriter writer = XmlWriter.Create(newStream, settings);
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.Load(originalStream);
List<XmlElement> longArrayItems = new List<XmlElement>();
Dictionary<string, XmlElement> multiRefs = new Dictionary<string, XmlElement>();
FindImportantNodes(xmlDocument.DocumentElement, longArrayItems, multiRefs);
FixLongArrays(longArrayItems, multiRefs);
xmlDocument.Save(writer);
newStream.Position = 0;
break;
}
}
private static void FindImportantNodes(XmlElement element, List<XmlElement> longArrayItems,
Dictionary<string, XmlElement> multiRefs)
{
string val = element.GetAttribute("soapenc:arrayType");
if (val != null && val.Contains(":long["))
{
longArrayItems.Add(element);
}
if (element.Name == "multiRef")
{
multiRefs[element.GetAttribute("id")] = element;
}
foreach (XmlNode node in element.ChildNodes)
{
XmlElement child = node as XmlElement;
if (child != null)
{
FindImportantNodes(child, longArrayItems, multiRefs);
}
}
}
private static void FixLongArrays(List<XmlElement> longArrayItems, Dictionary<string, XmlElement> multiRefs)
{
foreach (XmlElement element in longArrayItems)
{
foreach (XmlNode node in element.ChildNodes)
{
XmlElement child = node as XmlElement;
if (child != null)
{
string href = child.GetAttribute("href");
if (href == null || href.Length == 0)
{
continue;
}
if (href.StartsWith("#"))
{
href = href.Remove(0, 1);
}
XmlElement multiRef = multiRefs[href];
if (multiRef == null)
{
continue;
}
child.RemoveAttribute("href");
child.InnerXml = multiRef.InnerXml;
if (log.IsDebugEnabled)
{
log.Debug("Replaced multiRef id '" + href + "' with value: " + multiRef.InnerXml);
}
}
}
}
}
public override Stream ChainStream(Stream s)
{
originalStream = s;
newStream = new MemoryStream();
return newStream;
}
private static void CopyStream(Stream from, Stream to)
{
TextReader reader = new StreamReader(from);
TextWriter writer = new StreamWriter(to);
writer.WriteLine(reader.ReadToEnd());
writer.Flush();
}
}
Finally, we tag all methods in the Reference.cs file that will be deserializing a long array with our attribute:
[SoapRpcMethod("", RequestNamespace="http://some.service.provider",
ResponseNamespace="http://some.service.provider")]
[return : SoapElement("getFooReturn")]
[LongArrayHelper]
public Foo getFoo()
{
object[] results = Invoke("getFoo", new object[0]);
return ((Foo) (results[0]));
}
This fix is long-specific, but it could probably be generalized to handle any primitive type having this problem.

Here's a more or less copy-pasted version of a blog post I wrote on the subject.
Executive summary: You can either change the way .NET deserializes the result set (see Chris's solution above), or you can reconfigure Axis to serialize its results in a way that's compatible with the .NET SOAP implementation.
If you go the latter route, here's how:
... the generated
classes look and appear to function
normally, but if you'll look at the
deserialized array on the client
(.NET/WCF) side you'll find that the
array has been deserialized
incorrectly, and all values in the
array are 0. You'll have to manually
look at the SOAP response returned by
Axis to figure out what's wrong;
here's a sample response (again,
edited for clarity):
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv=http://schemas.xmlsoap.org/soap/envelope/>
<soapenv:Body>
<doSomethingResponse>
<doSomethingReturn>
<doSomethingReturn href="#id0"/>
<doSomethingReturn href="#id1"/>
<doSomethingReturn href="#id2"/>
<doSomethingReturn href="#id3"/>
<doSomethingReturn href="#id4"/>
</doSomethingReturn>
</doSomethingResponse>
<multiRef id="id4">5</multiRef>
<multiRef id="id3">4</multiRef>
<multiRef id="id2">3</multiRef>
<multiRef id="id1">2</multiRef>
<multiRef id="id0">1</multiRef>
</soapenv:Body>
</soapenv:Envelope>
You'll notice that Axis does not
generate values directly in the
returned element, but instead
references external elements for
values. This might make sense when
there are many references to
relatively few discrete values, but
whatever the case this is not properly
handled by the WCF basicHttpBinding
provider (and reportedly by gSOAP and
classic .NET web references as well).
It took me a while to find a solution:
edit your Axis deployment's
server-config.wsdd file and find the
following parameter:
<parameter name="sendMultiRefs" value="true"/>
Change it to false,
then redeploy via the command line,
which looks (under Windows) something
like this:
java -cp %AXISCLASSPATH% org.apache.axis.client.AdminClient server-config.wsdl
The web service's
response should now be deserializable
by your .NET client.

Found this link that may offer a better alternative: http://www.tomergabel.com/GettingWCFAndApacheAxisToBeFriendly.aspx

Related

How do I validate an annotation at compile time?

I have an annotation defined:
public #interface AdminMapping {
public final static int USER = 100;
public final static int COMPANY = 10;
public final static int ADMIN = 0;
int adminLevel() default AdminMapping.USER;
String displayName();
String category() default "";
String hasPermission() default "";
String parentCategory() default "";
}
I want to make it so you can't have a parentCategory unless you have a category....
#AdminMapping(category="Company", displayName="FOO", adminLevel=AdminMapping.USER)
public static final String MONKEY = "chimp";
#AdminMapping(parentCategory="Company", displayName="BAR", adminLevel=AdminMapping.USER) //NOT VALID Parent without Category
public static final String HORSE = "zebra";
while I'm at it I'd also like it if I could make it so the category doesn't equal the parentCategory.
I know how to use ConstraintValidator to validate MONKEY and HORSE but I want to validate the actual AdminMapping entries. Can this be done at compile time?
Any help is appreciated.
Use PMD. PMD is a tool that parses your source code and lets you check that it meets certain rules. It comes with lots of built in rules but you can write your own so you can write a rule to check your annotations are defined how you want.
See this page for an explanation of writing your own rules.
For example, it is easy to write a rule that checks whether an annotation has both parentCategory and category defined.
First declare the custom rule in a rules xml file:
<?xml version="1.0"?>
<ruleset name="My custom rules"
xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 http://pmd.sourceforge.net/ruleset_2_0_0.xsd">
<rule name="NoParent"
message="No parent category without category"
class="com.test.CategoryRule">
<description>
No parent category without category
</description>
</rule>
Then define a Java class that will do the actual enforcement.
public class CategoryRule extends AbstractJavaRule {
public Object visit(ASTMemberValuePairs node, Object data) {
boolean hasCategory = false;
boolean hasParentCategory = false;
for ( int i = 0; i < node.jjtGetNumChildren(); ++i ) {
if ( node.jjtGetChild(i).getImage().equals("category") ) {
hasCategory = true;
} else if ( node.jjtGetChild(i).getImage().equals("parentCategory") ) {
hasParentCategory = true;
}
}
if ( hasCategory && !hasParentCategory ) {
addViolation(data, node);
}
return data;
}
}
Obviously you'd need to write the code to be a bit more robust in reality, check the annotation type, but hopefully that enough to get you started.
You need to get a reference to the annotation and check its attributes
AdminMapping mapping = ...; // depends on what your annotation is annotating
if (mapping.category().isEmpty()) {
if (!mapping.parentCategory().isEmpty()) {
// we got a beef
}
}

Java - Trouble Accessing ValueMap

I've got a pretty simple script that simply accesses a resources properties via a ValueMap. Except for some reason I keep getting an unexpected token error were the if statement starts. This has to be something I'm just overlooking.
public class headerComponent{
ValueMap property = resource.adaptTo(ValueMap.class);
if(property != null) {
pageHeader = property.get("pageHeader", "");
}
}
Any ideas? Thanks for the help.
Because you are using if direct inside your class. This should be inside a function.
For Ex:
public class headerComponent{
ValueMap property = resource.adaptTo(ValueMap.class);
public void getMeProp()
{
if(property != null) {
pageHeader = property.get("pageHeader", "");
}
}
}
If you want to return your string then use public String getMeProp() and in the end of the function return pageHeader. Depends how you want to implement.

Android speed issue with deserialization using SimpleXML

Raised a bounty as the only answer doesn't provide a good implementation for Android. Is there a speedier implementation compatible with Android? Or is SimpleXML the best performance I'll get?
I'm fairly novice to Java and Android development so don't know the proper procedure for deserializing an xml string to an object. I found a method that works in:
public static Object deserializeXMLToObject(String xmlFile,Object objClass) throws Exception
{
try
{
InputStream stream = new ByteArrayInputStream(xmlFile.getBytes("UTF-8"));
Serializer serializer = new Persister();
objClass = serializer.read(objClass, stream);
return objClass;
}
catch (Exception e)
{
return e;
}
}
Where xmlFile is the (misnamed) xml string, and objClass is an empty class of the class I want to deserialize to. This is generally a list of other objects.
Example class:
#Root(name="DepartmentList")
public class DepartmentList {
#ElementList(entry="Department", inline=true)
public List<Department> DepartmentList =new ArrayList<Department>();
public boolean FinishedPopulating = false;
}
Department class:
public class Department {
#Element(name="DeptID")
private String _DeptID ="";
public String DeptID()
{
return _DeptID;
}
public void DeptID(String Value)
{
_DeptID = Value;
}
#Element(name="DeptDescription")
private String _DeptDescription ="";
public String DeptDescription()
{
return _DeptDescription;
}
public void DeptDescription(String Value)
{
_DeptDescription = Value;
}
}
Example XML:
<DepartmentList>
<Department>
<DeptID>525</DeptID>
<DeptDescription>Dept 1</DeptDescription>
</Department>
<Department>
<DeptID>382</DeptID>
<DeptDescription>Dept 2</DeptDescription>
</Department>
</DepartmentList>
This has been working fine throughout the app, but I have come to a point where it needs to deserialise >300 objects in the list. This only takes approximately 5 secs, or close to a minute when debugging, but users are not happy with that performance and time wasted when debugging isn't desirable. Is there any way to speed this up? Or is there another way I should be doing this? Preferably only by changing the deserializeXMLToObject method.
I am sure someone will point to a better library that's out there, but according to one detailed answer, they are all slow on Android.
So here is my quick hack (yes I know its not very maintainable and is brittle to the XML not being formed exactly as specified) and some results:
private void doTest()
{
Thread t = new Thread()
{
public void run()
{
runOne(2000);
runOne(300);
runOne(20000);
}
private void runOne(int num)
{
String start = "<DepartmentList>";
String mid1 = "<Department>\n" +
"<DeptID>";
String mid2 = "</DeptID>\n" +
"<DeptDescription>Dept ";
String mid3 = "</DeptDescription></Department>";
String fin = "</DepartmentList>";
StringBuffer sb = new StringBuffer();
sb.append(start);
for (int i=0; i< num; i++)
{
sb.append(mid1);
sb.append(""+i);
sb.append(mid2);
sb.append(""+i);
sb.append(mid3);
}
sb.append(fin);
Pattern p = Pattern.compile(
"<Department\\s*>\\s*<DeptID\\s*>([^<]*)</DeptID>\\s*<DeptDescription\\s*>([^<]*)</DeptDescription>\\s*</Department>");
long startN = System.currentTimeMillis();
DepartmentList d = new DepartmentList();
List<Department> departments = d.DepartmentList;
Matcher m = p.matcher(sb);
while (m.find())
{
Department department = new Department();
department.DeptID(m.group(1));
department.DeptDescription(m.group(2));
departments.add(department);
}
long endN = System.currentTimeMillis();
Log.d("Departments", "parsed: " + departments.size() + " in " + (endN-startN) + " millis");
Log.d("Departments", "lastone: " + departments.get(departments.size() -1)._DeptID + " desc: " + departments.get(departments.size() -1)._DeptDescription);
}
};
t.start();
}
public class DepartmentList {
public List<Department> DepartmentList =new ArrayList<Department>();
public boolean FinishedPopulating = false;
}
public class Department {
private String _DeptID ="";
public String DeptID()
{
return _DeptID;
}
public void DeptID(String Value)
{
_DeptID = Value;
}
private String _DeptDescription ="";
public String DeptDescription()
{
return _DeptDescription;
}
public void DeptDescription(String Value)
{
_DeptDescription = Value;
}
}
I pasted this into an Android project and called it from the onCreate() method. Here is the results:
Platform num=300 num=2000 num=20000
=================================================
Nexus 7 5 38 355
Galaxy Y 29 430 1173
HTC Desire HD 19 189 539
Galaxy Nexus 14 75 379
All times are in milliseconds.
For my research this is the best way to optimize:
"Simple will dynamically build your object graph, this means that it will need to load classes that have not already been loaded, and build a schema for each class based on its annotations using reflection. So first use will always be by far the most expensive. Repeated use of the same persister instance will be many times faster. So try to avoid multiple persister instances, just use one if possible."
So refactoring your code to use the same Persister should improve you performance.
This and other tips I got from this question. In that case, this refactoring improved the performance, as stated by the author (from 15s to 3-5s).
Hope it helps
You can eliminate the intermediate (de)serialization steps by serializing directly to XML and deserializing directly from XML, using e.g. JAXB or XStream.
You may also be able to speed things up via multithreading. I'll assume that all of the XML strings you want to deserialize are in a ConcurrentLinkedQueue; alternatively, you can synchronize access to whatever non-threadsafe collection you're using. Use something like a ThreadPoolExecutor to minimize thread creation overhead.
public class DeserializeXML implements Runnable {
private final String xml;
private final ConcurrentLinkedQueue<Object> deserializedObjects;
public DeserializeXML(String xml, ConcurrentLinkedQueue deserializedObjects) {
this.xml = xml;
this.deserializedObjects = deserializedObjects;
}
public void run() {
deserializedObjects.offer(deserializeXMLToObject(xml, Object.class));
}
}
// ***
ConcurrentLinkedQueue<String> serializedObjects;
ConcurrentLinkedQueue<Object> deserializedObjects;
ThreadPoolExecutor executor = new ThreadPoolExecutor;
while(!serializedObjects.isEmpty()) {
executor.execute(new DeserializeXML(serializedObjects.poll(), deserializedObjects));
}
Got a similar issue with a SOAP Web Service times ago.
In the end I've changed the format of the XML, transforming the nodes in attributes.
example:
<node>
<attr1>value1</attr1>
<attr2>value2</attr2>
<attr3>value3</attr3>
<attr4>value4</attr4>
</node>
was transformed in
<node attr1='value1'attr2='value2' attr3='value3' attr4='value4' />
Maybe not the best solution theorically wise, but performance improvement was really good. Ofc if your XML is a bit more complex (repeteable nodes at various levels or multi level lists) things may be a bit complex.
Use a server proxy and transform your XML to JSON

Parse xml from internet (yr.no)

I am not experienced in xml parsing so maybe some of the things I write look stupid to some and maybe some of my terminology is not quite correct.. Please forgive.
I develop an android app, which needs among others to parse weather data from YR.no. This organization offers an api with methods that provide certain data on xml format. Let’s say for example I want to parse xml data from this http://api.yr.no/weatherapi/seaapproachforecast/1.0/?location=stad
I developed a code that can do some xml parsing and it works right in this http://www.w3schools.com/xml/simple.xml (as a test).
The main code lines to define what to get in my BaseFeedParser class are:
RootElement root2 = new RootElement("breakfast_menu");
Element food = root2.getChild("food");
Element name = food.getChild("name");
food.setEndElementListener(new EndElementListener() {
public void end() {
messages.add(currentMessage.copy());
}
});
food.getChild("name").setEndTextElementListener(new EndTextElementListener() {
public void end(String body) {
currentMessage.setTitle(body);
}
});
try {
Xml.parse(this.getInputStream(), Xml.Encoding.ISO_8859_1, root2.getContentHandler());
} catch (Exception e) {
throw new RuntimeException(e);
}
return messages;
And then from my activity class:
#Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
loadFeed();
}
private void loadFeed() {
try {
BaseFeedParser parser = new BaseFeedParser();
messages = parser.parse();
List<String> titles = new ArrayList<String>(messages.size());
System.out.println(messages.size());
for (Message msg : messages) {
titles.add(msg.getTitle());
}
ArrayAdapter<String> adapter =
new ArrayAdapter<String>(this, R.layout.row,titles);
this.setListAdapter(adapter);
String str = "!";
if (titles != null) {
str = titles.toString();
System.out.println("not null");
System.out.println(str);
}
test(str);
} catch (Throwable t) {
Log.e("AndroidNews",t.getMessage(), t);
}
}
public void test(String s) {
setContentView(R.layout.error);
TextView textView = (TextView) findViewById(R.id.mytextview);
textView.setText(s);
}
So it returns and prints the data I want (“Belgian Waffles” etc)
My problem with the yr.no data that I originally wanted to parse is that every end child does not contain just one value but can have more tags (e.g. <waveDirection unit="degree" value="250"/>). So when I change the elements to use this one, it ends to 25 different Strings (which if you count are all the different children with tag waveDirection) but every value is empty (like a String a = ""). I get no error, I just get a list of 25 empty strings. The way I try to reach my element is something like:
RootElement root = new RootElement("weatherdata");
Element product = root.getChild("product");
Element time = product.getChild("time");
Element location = time.getChild("location");
location .setEndElementListener(new EndElementListener(){
public void end() {
messages.add(currentMessage.copy());
}
});
location.getChild("windDirection").setEndTextElementListener(new EndTextElementListener() {
public void end(String body) {
currentMessage.setTitle(body);
}
});
So how should I modify this so that it works with this xml? I do not provide all the classes and methods (like setTitle()) but I think they work since they parse right my first test xml. And I suppose I set my feedUrlString = "http://api.yr.no/weatherapi/seaapproachforecast/1.0/?location=stad"; correctly since it finds the root of the document and 25 elements.
EDIT: I did it! The right way to get the attributes was to use:
location.setStartElementListener(new StartElementListener(){
public void start(Attributes attributes){
messages.add(currentMessage.copy());
}
});
location.getChild("windDirection").setTextElementListener(new TextElementListener(){
public void end(String body) {
//currentMessage.setTitle(body);
//System.out.println("b="+ body);
}
#Override
public void start(Attributes attributes) {
System.out.println("val" + attributes.getValue("deg"));
currentMessage.setTitle(attributes.getValue("deg"));
}
});
So now I get my data but for some reason all except the very last element (I tested it for other YR.no xmls as well).. There must be some bug that I should solve but the major step is done. Thank you all for the answers and especially user306848 who pointed me to the direction I used!
Use Dom Parser.......It will be easy...
See some tutorial from here,
http://www.roseindia.net/xml/dom/
I think your code assumes there is text node which represents the values you seek. Which is not the case for the xml file from the yr.no domain.
You need to figure out how to read attributes from tags for the xml library you use.
I have no experience with android development, but do you use the android.sax package? Then i think android.sax.StartElementListener would be a good candidate. It receives the attributes that belong to a specific tag.
Use this example.
He is using a XML file stored locally. If your getting a XML Feed change the code to the following:
URL url = new URL("ENTER XML LINK");
InputStream stream = url.openStream();
Document doc = docBuilder.parse(stream);

Jackson Change JsonIgnore Dynamically

I have a class and there are variables inside it as well. Sometimes I want to ignore some fields and sometimes not when deserializing (maybe at serializing too). How can I do it at Jackson?
For serialization, "filtering properties" blog entry should help. Deserialization side has less support, since it is more common to want to filter out stuff that is written.
One possible approach is to sub-class JacksonAnnotationIntrospector, override method(s) that introspect ignorability of methods (and/or fields) to use whatever logic you want.
It might also help if you gave an example of practical application, i.e what and why you are trying to prevent from being deserialized.
You might want to use JsonViews ( took it originally from http://wiki.fasterxml.com/JacksonJsonViews - broken now - web archive link: https://web.archive.org/web/20170831135842/http://wiki.fasterxml.com/JacksonJsonViews )
Quoting it:
First, defining views means declaring classes; you can reuse existing ones, or just create bogus classes -- they are just view identifiers with relationship information (child inherits view membership from parents):
// View definitions:
class Views {
static class Public { }
static class ExtendedPublic extends PublicView { }
static class Internal extends ExtendedPublicView { }
}
public class Bean {
// Name is public
#JsonView(Views.Public.class) String name;
// Address semi-public
#JsonView(Views.ExtendPublic.class) Address address;
// SSN only for internal usage
#JsonView(Views.Internal.class) SocialSecNumber ssn;
}
With such view definitions, serialization would be done like so:
// short-cut:
objectMapper.writeValueUsingView(out, beanInstance, ViewsPublic.class);
// or fully exploded:
objectMapper.getSerializationConfig().setSerializationView(Views.Public.class);
// (note: can also pre-construct config object with 'mapper.copySerializationConfig'; reuse)
objectMapper.writeValue(out, beanInstance); // will use active view set via Config
// or, starting with 1.5, more convenient (ObjectWriter is reusable too)
objectMapper.viewWriter(ViewsPublic.class).writeValue(out, beanInstance);
and result would only contain 'name', not 'address' or 'ssn'.
You should probably look at the modules feature of recent Jackson versions.
One possible mechanism would be to use a BeanDeserializerModifier.
I've been looking for a useful online tutorial or example, but nothing immediately appears. It might be possible to work something up if more is known of your context. Are you managing your ObjectMappers manually, or using them in a JAX-RS setting, injected in Spring, or what?
I searched the entire web (yes I did) to find the answer. then I wrote something on my own.
I'm working with Jackson ion deserialisation. I wrote a custom reader that ignores the fields dynamically.
You can do the same thing for json deserialisation.
Lets assume an entity like this.
User {
id
name
address {
city
}
}
Create a tree structure to represent field selection.
public class IonField {
private final String name;
private final IonField parent;
private final Set<IonField> fields = new HashSet<>();
// add constructs and stuff
}
Custom Ion Reader extending from amazon ion-java https://github.com/amzn/ion-java
public class IonReaderBinaryUserXSelective extends IonReaderBinaryUserX {
private IonField _current;
private int hierarchy = 0;
public IonReaderBinaryUserXSelective(byte[] data, int offset, int length,
IonSystem system, IonField _current) {
super(system, system.getCatalog(), UnifiedInputStreamX.makeStream(data, offset, length));
this._current = _current;
}
#Override
public IonType next() {
IonType type = super.next();
if (type == null) {
return null;
}
String file_name = getFieldName();
if (file_name == null || SystemSymbols.SYMBOLS.equals(file_name)) {
return type;
}
if (type == IonType.STRUCT || type == IonType.LIST) {
IonField field = _current.getField(getFieldName());
if (field != null) {
this._current = field;
return type;
} else {
super.stepIn();
super.stepOut();
}
return next();
} else {
if (this._current.contains(file_name)) {
return type;
} else {
return next();
}
}
}
#Override
public void stepIn() {
hierarchy = (hierarchy << 1);
if (getFieldName() != null && !SystemSymbols.SYMBOLS.equals(getFieldName())) {
hierarchy = hierarchy + 1;
}
super.stepIn();
}
#Override
public void stepOut() {
if ((hierarchy & 1) == 1) {
this._current = this._current.getParent();
}
hierarchy = hierarchy >> 1;
super.stepOut();
}
Construct dynamic view. This Tree dynamically created and passed to the reader to deserialise.
Let's say we only need city inside the address.
IonField root = new IonField("user", null);
IonField address = new IonField("address", root);
IonField city = new IonField("city", address);
address.addChild(city);
root.addChild(id);
//now usual stuff.
IonFactory ionFactory = new IonFactory();
IonObjectMapper mapper = new IonObjectMapper(ionFactory);
File file = new File("file.bin"); // ion bytes
byte[] ionData = Files.readAllBytes(file.toPath());
IonSystem ionSystem = IonSystemBuilder.standard().build();
IonReader ionReader = new IonReaderBinaryUserXSelective(ionData, 0, ionData.length, ionSystem, root);
User user = mapper.readValue(ionReader, User.class);

Categories