Extracting associated JSON data using the unique key - java

I'm new to Java and have used the long piece of code provided in
GWT: Dealing with incoming JSON string
to read the json similar to the layout of the original posting person raised.
My layout is as follows:
{
"messagedata": [
{
"msgkey": "12552",
"reference": "201708010001",
"bic": "PARABLULLEISI",
"securityid": "BE0003735496",
"safekeepingacc": "7744085P"
},
{
"msgkey": "12553",
"reference": "000081676368",
"bic": "PARABLULLEISX",
"securityid": "CNE00000BQ0",
"safekeepingacc": "1053542760H"
}
]
}
But the final line of code (jsonString.stringValue()) only ever reads the first block of JSON data i.e. msgkey or bic from the first section.
How would i get data from other sections i.e. if there were 3 sections each containing msgkey, bic, reference etc
More importantly if i know the msgkey value as in the sample JSON how can I get the other associated values for that section when the msgkey value changes?
I've used the library com.google.gwt.json.client.*
Thanks
Martin

You can use JsInterop and JSON.parse in GWT 2.8 + elemental2.
import com.google.gwt.core.client.EntryPoint;
import elemental2.core.Global;
import elemental2.dom.DomGlobal;
import java.util.stream.Stream;
import jsinterop.annotations.*;
import jsinterop.base.Js;
class JsInteropExample implements EntryPoint {
#JsType(isNative = true, namespace = JsPackage.GLOBAL, name = "Object")
static class Message {
public Data[] messagedata;
}
#JsType(isNative = true, namespace = JsPackage.GLOBAL, name = "Object")
static class Data {
public String msgkey;
public String reference;
public String bic;
public String securityid;
public String safekeepingacc;
}
#Override public void onModuleLoad() {
Message msg = Js.cast(Global.JSON.parse("{\"messagedata\": […]}"));
Stream.of(msg.messagedata).forEach(d -> DomGlobal.console.log(d.msgkey));
}
}

I totally agree with Ignacio, JsInterop is the way, that is why I was asking about GWT version.
JsInterop will automatically map getter and setter to the right property as you can see below.
It also allow you to add java overlay methods to your native objects, which I personally find very convenient and clean.
In order to have this code working you need to make sure to have elemental2 and jsinterop imported in your gwt.xml files.
import com.google.gwt.core.client.EntryPoint;
import elemental2.core.Global;
import elemental2.dom.DomGlobal;
import java.util.stream.Stream;
import jsinterop.annotations.*;
import jsinterop.base.Js;
class JsInteropExample implements EntryPoint {
#JsType(isNative = true, namespace = JsPackage.GLOBAL, name = "Object")
static abstract class Message {
#JsProperty
public abstract Data[] getMessagedata();
#JsOverlay
public void logObject(){
Stream.of(getMessagedata).forEach(d -> DomGlobal.console.log(d.msgkey));
}
}
#JsType(isNative = true, namespace = JsPackage.GLOBAL, name = "Object")
static class Data {
public String msgkey;
public String reference;
public String bic;
public String securityid;
public String safekeepingacc;
}
#Override public void onModuleLoad() {
Message msg = Js.cast(Global.JSON.parse("{\"messagedata\": […]}"));
Stream.of(msg.messagedata).forEach(d -> DomGlobal.console.log(d.msgkey));
}
}
If you want to avoid using elemental2 you can decode the Json by using:
#JsMethod(namespace="JSON")
static native DivData parse(String json);

Related

Java JAXB marshall/unmarshall using Java Optionals

My applications needs to convert data between Java and XML.
When converting the data, I need to distinguish whether or not the value was present, the value was set explicitly to null or the value had a value.
XML example:
<person><name>Bob</name></person> <-- element 'name' contains value "Bob"
<person><name nil="true"/></person> <-- element 'name' was set explicitly to 'nil'/null
<person></person> <-- element 'name' is missing
As Java types like 'String' only knows two states (null or not null), I tried to use Java Optionals to solve this.
A mapping between XML and Java Optionals could look like this:
<person></person> <=> Optional<String> name = null;
<person><name>Bob</name></person> <=> Optional<String> name = Optional.of("Bob");
<person><name nil="true"/></person> <=> Optional<String> name = Optional.empty();
I tried to use JAXB for the marshalling and unmarshalling. The idea was that the setter of a field only gets invoked when a value needs to be set explicitly to an value. That means that a value is absent implicitly.
I had a look on other stackoverflow questions like the following, but all of them were incomplete handling the behaviour I need to achieve:
How to generate JaxB-Classes with java.util.Optional?
Using generic #XmlJavaTypeAdapter to unmarshal wrapped in Guava's Optional
Using Guava's Optional with #XmlAttribute
I've been struggling with this problem for two days now. I tried to use the XMLAdapter and GenericAdapter, tried several ways how to annotate the fields and getter/setter with #XmlElement, tried to use #XmlAnyElment with and without lax, but all of them only led to a partial success. Either the nil value was not handeld correctly, the lists were not printed out correctly, ...
I think every Java webservice with a properly implemented patch operation should have had this problem. (not talking about the "json patch approach" (RFC 6902))
Is there a common way to solve my problem?
The following code is able to distinguish empty name from null name. To make the solution work, I created a PersonList element to contain all of the person elements. Each Person contains a Name that will have isNil() return true if the element was explicitly set to null by the XML:
Person.java:
import java.util.Optional;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
#XmlType(propOrder = {"name"})
#XmlRootElement(name = "person")
public class Person {
private Optional<Name> optionalName;
public Person() {
optionalName = Optional.<Name>empty();
}
public Optional<Name> getOptionalName() {
return optionalName;
}
public Name getName() {
return (optionalName.isPresent()) ? (optionalName.get()) : (null);
}
#XmlElement(name = "name", required = false)
public void setName(Name name) {
optionalName = Optional.ofNullable(name);
}
#Override
public String toString() {
return String.format("Person(optionalName.isPresent() = %s, name = %s)",
Boolean.toString(optionalName.isPresent()),
((getName() == null) ? ("null") : (getName().toString())));
}
}
Name.java:
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlValue;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "name")
public class Name {
#XmlAttribute(name = "nil")
private boolean nil;
#XmlValue
private String value;
public Name() {
nil = false;
value = null;
}
public boolean isNil() {
return nil;
}
public void setNil(boolean torf) {
this.nil = torf;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
#Override
public String toString() {
return String.format("Name(nil = %s, value = %s)",
Boolean.toString(nil),
(value == null) ? ("null"):("\""+getValue()+"\""));
}
}
PersonList.java:
import java.util.Iterator;
import java.util.List;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name = "PersonList")
public class PersonList {
private List<Person> persons;
public PersonList() {
persons = null;
}
#XmlElement(name = "person")
public List<Person> getPersons() {
return persons;
}
public void setPersons(List<Person> persons) {
this.persons = persons;
}
#Override
public String toString() {
StringBuilder sb = new StringBuilder("PersonList(persons = ");
if(persons == null) {
sb.append("null");
}
else {
sb.append("[");
Iterator<Person> iterator = persons.iterator();
while(iterator.hasNext()) {
sb.append(iterator.next().toString());
if(iterator.hasNext()) {
sb.append(", ");
}
}
sb.append("]");
}
sb.append(")");
return sb.toString();
}
}
Main class to demonstrate the solution:
import java.io.ByteArrayInputStream;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
public class XmlOptional {
public static final int STATUS_OKAY = 0;
public static final int STATUS_ERROR = -1;
public static final String XML_DATA = "<PersonList>" +
"<person><name>Bob</name></person>" +
"<person><name nil=\"true\" /></person>" +
"<person></person>" +
"</PersonList>";
private XmlOptional() {
}
private static PersonList loadXml() {
try {
ByteArrayInputStream bais = new ByteArrayInputStream(XML_DATA.getBytes());
JAXBContext context = JAXBContext.newInstance(PersonList.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
PersonList personList = (PersonList)unmarshaller.unmarshal(bais);
return personList;
}
catch(Exception e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) {
int status = STATUS_OKAY;
try {
PersonList personList = loadXml();
System.out.format("Xml contained: %s%n", personList);
}
catch (Throwable thrown) {
status = STATUS_ERROR;
thrown.printStackTrace();
}
finally {
System.exit(status);
}
}
}
Sample output:
Xml contained: PersonList(persons = [Person(optionalName.isPresent() = true, name = Name(nil = false, value = "Bob")), Person(optionalName.isPresent() = true, name = Name(nil = true, value = "")), Person(optionalName.isPresent() = false, name = null)])
Since I was not able to solve the problem completely by solely using and configuring JAXB properly, I decided to solve it as follows:
(The main goal was to write a subsystem to communicate with an external system based on XML)
As a starting point, I used the XSD schema provided by the target system to communicate with and generated the corresponding (XML)Java classes using JAXB and the XSD file. All the fields in those generated classes were of type JAXBElement<>, in order to be able to hold the 3 states needed (absent, null, someValue).
On the business model side, I used Java classes with Optional<> field types in order to hold the 3 states.
For the mapping, I wrote a mapper which uses reflection to recursively map from JAXB to Java and vice versa. When mapping from Java to JAXB, the mapper used the ObjectFactory to create the JAXBElement objects. (Mapper itself just had about 300 lines of code).
The fields were mapped based on the matching field names.
The most ugly and challenging part was, that the XSD schema file needed to be altered, in order to make JAXB generated classes that uses JAXBElement field types. Therefore I had to manually add the attribute minOccurs="0" nillable="true" to the XML elements, if not already set.
With that solution above, I finally managed to map the XML to Java and vice versa considering the 3 states needed, easily.
Of course, this solution has its drawbacks.
One is the manual modification of the XSD file. Usually bad practice to alter the XSD file provided by the external system, which acts as an interface contract.
For my requirements at the time, the solution worked perfectly. Even changes to the interface contract of the external system could be implemented very easily.
You can use some validation in your java class like #NotNull, #Size and so on. Or you can put default value , to be sure , that it will be not null. After that you can create DTOs (Data transfer object) with the recommended Xml annotations and mapped it with the ModelMapper.

Converting List to JSON in Java with correct extension

I am trying to create a List of errors with different codes and converting them to JSON using gson.
String jsonString = gson.toJson(Errors.getErrors());
And here is the class:
public class Errors {
private static List<SingleError> errors = new ArrayList<>();
public static List<SingleError> getErrors() {
return errors;
}
public static void addError(SingleError singleError) {
errors.add(singleError);
}
}
The output I get:
[
{
"code": "bad_signal"
}
]
The output I need:
{
"errors": [
{
"code": "bad_signal"
}
]
}
What am I missing in the Errors class the get the output I need?
It would be better if it would be added in the class without just adding string to json conversion.
EDIT
As schomsel suggested, I should use this line to get the output I need.
gson.toJson(Collections.singletonMap("errors", Errors.getErrors()))
And it did work but I failed to mention that I am also using Servlet to return the String and setting this header, which deletes the "errors".
resp.setHeader("Content-Type", "application/json");
What is the correct header I need to use here?
Obviously, you should understand that desired json representation is for Errors class itself and not contained erros list only so your code is to be tweaked so you can pass Errors class instance as input to - gson.toJson(...)
Two solutions ,
First Solution - make Errors fields and methods non - static and pass on Errors instance instead of errors List to call - gson.toJson(ErrorsInstance);
import java.util.ArrayList;
import java.util.List;
import com.google.gson.Gson;
class Errors {
private List<SingleError> errors = new ArrayList<>();
public List<SingleError> getErrors() {
return errors;
}
public void addError(SingleError singleError) {
errors.add(singleError);
}
Second Solution - if fields & methods can't be made static then add a new method to get Errors instance via private constructor and then create Gson object from GsonBuilder so that static fields can be included during serialization.
import java.util.ArrayList;
import java.util.List;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
class Errors {
// private constructor
private Errors(List<SingleError> errors) {
Errors.errors = errors;
}
private static List<SingleError> errors = new ArrayList<>();
public static List<SingleError> getErrors() {
return errors;
}
// New method to return Errors instance
public static Errors getErrorsInstance() {
return new Errors(errors);
}
public static void addError(SingleError singleError) {
errors.add(singleError);
}
}
//To include static fields during serialization & ignore only transient fields- if not done then json would be empty
GsonBuilder gsonBuilder = new GsonBuilder();
// Allowing the serialization of static fields
gsonBuilder.excludeFieldsWithModifiers(java.lang.reflect.Modifier.TRANSIENT);
// Creates a Gson instance based on the current configuration
Gson gson = gsonBuilder.create();
Errors errorsInstance = Errors.getErrorsInstance();
String jsonStringTest = gson.toJson(errorsInstance );
EDIT:
For Second solution, you wouldn't need a private constructor & new method - getErrorsInstance() . You can simply feed new Errors() to gson.toJson(...) . What you need is only static field inclusion in deserialization & same would hold true for solution # 1 too. So you don't need to modify any code, just make sure with GsonBuilder that static fields are included & pass on Errors instance instead of contained list.

Best way to access properties from property file in java

I have properties file app.properties and it has 50 different properties.
I am accessing those in my java class using
Properties prop = new Properties();
prop.load("app.properties");
System.out.prinltn(prop.getProperty("APPNAME"));
Actually, I want to get rid of accessing property like prop.getProperty("APPNAME"). Is there any best way in java to access properties.
I can declare all variables as static in java class.
static String appName = prop.getProperty("APPNAME");
Any other best way available?
I can suggest two approaches:
1. Define a utility method which will take String as parameter and return value from properties.
For Example:
public static String GetValue(String key) {
return properties.getProperty(key);
}
And now you can use this function on callers
String value = GetValue("key"); // properties.getProperty("key");
Define above method and in addition create one class Called Constants(or something suitable). Define all your Keys here as Static final variable.
public class Constants
{
public static final String KEY = "key";
public static final String KEY2 = "key2";
}
and now make call for getting value using these variable instead of string:
String value = GetValue(KEY); //GetValue("key");
If you do only option 1, your code is becoming more readable. But I will recommend 2nd option, which is making your code readable as well as maintainable.
You can easily do following operation :
Update property name
No need to worry about mistyping key etc.
You may use "resourceBundle" package as
First import the resourceBundle API:
import java.util.ResourceBundle;
Create an instance of your property file:
private static ResourceBundle resource = ResourceBundle.getBundle("app");
Now you can get the value of the property:
String appName = resource.getString("APPNAME");
IMO, your approach of using static variables to hold the values is the best. The following structure was what I was using in a project for the same functionality.
package snippet;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Properties;
public class Constants {
public static final String APPNAME;
public static final String VERSION;
public static final int DEFAULT_TIMEOUT;
static {
Properties p = new Properties();
try {
p.load(new FileInputStream("constants.properties"));
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
APPNAME = p.getProperty("APPNAME");
VERSION = p.getProperty("VERSION");
DEFAULT_TIMEOUT = Integer.parseInt(p.getProperty("DEFAULT_TIMEOUT"));
}
}
Of course, there were checks for NumberFormatException etc.

stream a large String into JAXB

I have a domain object in my JAXB hierarchy which must be represented as comma separated value text. Unfortunately, explicitly constructing the CSV String is incredibly costly so that is not an option.
I created a custom #XmlJavaTypeAdapter that returned a DataHandler (as per supported data types) but that always writes the data out in BASE64... but I have a legacy API to preserve that expects the ASCII string in there. Changing the MIME of the DataHandler doesn't change the encoding, but it would impact the XSD's definition of the object contained within.
Is there any way to setup DataHandler (or any other supported Java type) to return the un-encoded String from a streaming input?
I also considered returning an Object (which was really a CharacterData) but that needs to implement public String getData()... requiring me to explicitly construct the String that I'm trying to stream.
In case no one comes up with DataHanler-related solution... The following is just an alternative idea for a "work-around" which does not involve DataHandler. It requires access to the marshaller.
Modify your XML type adapter to not return the content but a kind of short address to get hold of the streaming data (e.g. a file name).
Define a XMLStreamWriter wrapper like here: JAXB marshalling XMPP stanzas. Overwrite the writeStartElement and writeCharacters to intercept the startElement invocation of the CSV element and the immediately following writeCharacters.
The data passed to that specific invocation of writeCharacters will be the address to get hold of the streaming data. Stream it in chunks to the wrapped XMLStreamWriter's writeCharacters.
I don't quite understand why explicitly constructing the CSV string (using StringBuilder) would be more costly than using JAXB builtins.
If the performance is your limiting factor, then I think you should consider creating custom serializers (StringBuilder based, for example) and SAX handlers to parse the XML.
If you have the luxury of changing the protocol, then you might want to check out Grizzly framework, Avro and Google ProtoBuf - there's quite a bit more maintenance with them, but if you are going after performance then these should be faster.
As always, you should do A/B performance tests using both methods before setting anything into stone ;)
Back to the original topic, here's an example on how to use custom adapters:
import static org.junit.Assert.assertEquals;
import java.io.StringWriter;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import org.junit.Test;
public class Example
{
public String serialize( DataObject d ) throws JAXBException {
StringWriter buffer = new StringWriter();
JAXBContext.newInstance(DataObject.class).createMarshaller().marshal(d, buffer);
return buffer.toString();
}
#Test
public void testSerialize( ) throws JAXBException {
String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><dataObject>"
+ "<FirstField>field1 content with special characters &<>'\"</FirstField>"
+ "<Second><!CDATA[[ <!-- now we're just nasty --> ]]></Second>"
+ "<Custom>a,b,c</Custom></dataObject>";
assertEquals(expected, serialize(new DataObject()).replaceAll("(\r)?\n(\r)?", "\n"));
}
}
#XmlRootElement
#XmlAccessorType( XmlAccessType.FIELD )
class DataObject
{
#XmlElement( name = "FirstField" )
private final String field1 = "field1 content with special characters &<>'\"";
#XmlElement( name = "Second" )
private final String field2 = "<!CDATA[[ <!-- now we're just nasty --> ]]>";
#XmlElement( name = "Custom" )
#XmlJavaTypeAdapter( value = CustomAdapter.class )
// you can move this over the type
private final CustomType type = new CustomType("a", "b", "c");
}
#XmlAccessorType( XmlAccessType.FIELD )
class CustomType
{
private final String a;
private final String b;
private final String c;
public CustomType( String a, String b, String c ) {
this.a = a;
this.b = b;
this.c = c;
}
public String getA( ) {
return a;
}
public String getB( ) {
return b;
}
public String getC( ) {
return c;
}
}
class CustomAdapter extends XmlAdapter<String, CustomType>
{
#Override
public String marshal( CustomType v ) throws Exception {
return String.format("%s,%s,%s", v.getA(), v.getB(), v.getC());
}
#Override
/** Please don't use this in PROD :> */
public CustomType unmarshal( String v ) throws Exception {
String[] split = v.split(",");
return new CustomType(split[ 0 ], split[ 1 ], split[ 2 ]);
}
}
This should get you going, unless I completely misunderstood your question.

Simple Xml - order of elements not preserved?‏

I am using SimpleXml 2.6.1 in my android app. Eventhough the documentation (http://simple.sourceforge.net/download/stream/doc/javadoc/index.html?org/simpleframework/xml/Order.html) says the order of the elements in the xml are same as the way they have defined in the class file, I am always getting the order to be random in the xml. If I add few more variables, the order of the elements again changes.
Adding #Order notation works, but since the class is complex with 100s of variables, I do not want to add order. Is this a known bug for android versions? It works fine in java console programs.
p.s: I opened the .class file disassembled and found the variables declared in the same order as java file, so I don't think it's a class file issue.
import org.simpleframework.xml.Element;
import org.simpleframework.xml.Order;
#Order(elements = {"name", "isTrue"})
public class SimpleXml {
public static final String NAME = "$NAME$";
public static final String IS_TRUE = "$IS_TRUE$";
#Element
private String name;
#Element
private Boolean isTrue;
...
Since there is no answer, I'll try to save precious time to anyone who gets here.
I found no cause, and since I don't have time to analyze Simple libraries, I came up with a "workaroud". It's more of an advice, actually - don't use it for (marshaling)creating xml if you have a large xml definition and the order matters(a rule more than an exception). The order is mostly used for marshaling anyway so just save yourself some time and do it manually.
The template:
<document>
<name>$NAME$</name>
<isTrue>$IS_TRUE$</isTrue>
</document>
The class:
import org.apache.commons.io.IOUtils;
import java.io.IOException;
import java.io.InputStream;
/**
* User: ksaric
*/
public class SimpleXml {
public static final String NAME = "$NAME$";
public static final String IS_TRUE = "$IS_TRUE$";
private String name;
private Boolean isTrue;
public SimpleXml() {
}
public Boolean getTrue() {
return isTrue;
}
public void setTrue(Boolean aTrue) {
isTrue = aTrue;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public String toString() {
String template = null;
try {
template = getTemplate();
} catch (IOException e) {
e.printStackTrace();
}
/* GUAVA - checkNotNull() */
if (null == template) return null;
template = template.replace(NAME, getName());
/* OR CONVERT IN THE GETTER METHOD */
template = template.replace(IS_TRUE, getTrue().toString());
return template;
}
/* SINGLETON? Performance(IO) loss... */
public String getTemplate() throws IOException {
InputStream templateStream = getClass().getResourceAsStream("/template.xml");
/* APACHE IO COMMONS */
/*
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-io</artifactId>
<version>1.3.2</version>
</dependency>
*/
final String stringTemplate = IOUtils.toString(templateStream);
return stringTemplate;
}
}
The test:
import org.junit.Test;
import static junit.framework.Assert.*;
/**
* User: ksaric
*/
public class SimpleXmlTest {
#Test
public void test() throws Exception {
//Before
/* Use standard instantiation, factory method recommended for immutability */
SimpleXml simpleXml = new SimpleXml();
simpleXml.setName("This is a name");
simpleXml.setTrue(false);
//When
String result = simpleXml.toString();
//Then
assertNotNull(result);
System.out.println(result);
}
}
Not really an answer, but save yourself some time and don't use Simple(which is a great library) on Android...
Simple Xml doesn't preserve Order on Android. Based on pfh's answer, here is my recommendation:
I would prefer to use JAXB in the case where you want the order to be preserved than manual string/template parsing. JAXB is slightly complex to use than SimpleXml but comes with similar set of annotations based xml serialization and deserialization.

Categories