How to parse this XML SOAP response to a POJO? - java

I'm having a problem converting a XML SOAP return to a corresponding POJO class. The XML return looks like this:
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
<env:Header></env:Header>
<env:Body>
<ns2:teste xmlns:ns2="http://teste.com.br/">
<retorno>
<codigoRetorno>000</codigoRetorno>
<descricao>Consulta Realizada com Sucesso</descricao>
<item>
<a>teste</a>
<b>teste</b>
<c>teste</c>
</item>
<item>
<a>teste</a>
<b>teste</b>
<c>teste</c>
</item>
</retorno>
</ns2:teste >
</env:Body>
</env:Envelope>
I tried to use the Jackson XMLmapper, but I can not get it to consider the 'RETURN' node as the ROOT element during deserialization. it considers the 'Envelope' node as ROOT node.
I need to extract just the return node and convert to my pojo class.
Another problem is that 'item' nodes should be part of a collection, however there is no parent node grouping these elements.
Does anyone know of a parser that does deserialization of this type of xml?

You can incorporate a streaming XML Parser (StAX) and XmlMapper in this way:
import java.io.StringReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
public class Deser {
// #formatter:off
private static final String JSON = " <env:Envelope xmlns:env=\"http://schemas.xmlsoap.org/soap/envelope/\">\n" +
" <env:Header></env:Header>\n" +
" <env:Body>\n" +
" <ns2:teste xmlns:ns2=\"http://teste.com.br/\">\n" +
" <retorno>\n" +
" <codigoRetorno>000</codigoRetorno>\n" +
" <descricao>Consulta Realizada com Sucesso</descricao>\n" +
" <item>\n" +
" <a>teste</a>\n" +
" <b>teste</b>\n" +
" <c>teste</c>\n" +
" </item>\n" +
" <item>\n" +
" <a>teste</a>\n" +
" <b>teste</b>\n" +
" <c>teste</c>\n" +
" </item>\n" +
" </retorno>\n" +
" </ns2:teste >\n" +
" </env:Body>\n" +
"</env:Envelope>";
// #formatter:on
private static final String TARGET_ELEMENT = "retorno";
public static void main(String[] args) throws Exception {
XmlMapper xmlMapper = new XmlMapper();
xmlMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
XMLInputFactory f = XMLInputFactory.newFactory();
XMLStreamReader sr = f.createXMLStreamReader(new StringReader(JSON));
while (sr.hasNext()) {
int type = sr.next();
if (type == XMLStreamReader.START_ELEMENT && TARGET_ELEMENT.equals(sr.getLocalName())) {
Retorno r = xmlMapper.readValue(sr, Retorno.class);
System.out.println(r.getDescricao());
}
}
}
}
class Retorno {
private int codigoRetorno;
private String descricao;
public int getCodigoRetorno() {
return codigoRetorno;
}
public void setCodigoRetorno(int codigoRetorno) {
this.codigoRetorno = codigoRetorno;
}
public String getDescricao() {
return descricao;
}
public void setDescricao(String descricao) {
this.descricao = descricao;
}
}
This yields:
Consulta Realizada com Sucesso
Adapt the code as necessary, this is just to prove out how to get it to do what you need!

The cleanest solution I found was using JSOUP:
private <T> T parseResponse(HttpEntity entity, Class<T> typeTarget) throws Exception {
try {
String xmlSoapResponse = EntityUtils.toString(entity, StandardCharsets.UTF_8);
String xmlRetorno = extractXmlElement(xmlSoapResponse, "retorno");
XmlMapper xmlMapper = new XmlMapper();
xmlMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
xmlMapper.registerModule(new JavaTimeModule());
return xmlMapper.readValue(xmlRetorno.toString(), typeTarget);
} catch (Exception e) {
throw new Exception("Fail during parser", e);
}
}
private String extractXmlElement(String xmlString, String nodeTagNameElement) {
Document document = Jsoup.parse(xmlString, "", Parser.xmlParser());
document.outputSettings().prettyPrint(false);
Elements retorno = document.getElementsByTag(nodeTagNameElement);
return retorno.toString();
}

Related

Junit test for unmarshalling method

I have to test my methods for unmarshalling functionality and I don't know how to do it. Can someone explain me?
Here are my methods:
#Service
public class MapperServiceImpl implements MapperService {
public void mappingContent(String directoryPath, String fileName) throws JAXBException {
File xmlFile = new File(directoryPath + "/" + fileName);
JAXBContext jaxbContext = JAXBContext.newInstance(Menu.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
Menu menu = (Menu) jaxbUnmarshaller.unmarshal(xmlFile);
displayMappingContent(menu);
}
public void displayMappingContent(Menu menu) {
List<Content> contents = menu.getContents();
AtomicInteger count = new AtomicInteger(1);
System.out.println("Type : " + menu.getType().toUpperCase());
contents.stream()
.flatMap(content -> content.getFoods().stream()).toList()
.forEach(food -> System.out.println("NR. " + count.getAndIncrement()
+ "\nName : " + food.getName() + "\nPrice : " + food.getPrice()
+ "\nDescription : " + food.getDescription() + "\nCalories" + food.getCalories() + "\n"));
}
}
Your code is hard to test because your methods operate by side effects (that is, printing to a PrintStream) instead of returning values.
A simple approach to make your code more testable is to create two more methods in your class:
#Service
public class MapperServiceImpl implements MapperService {
public void mappingContent(String directoryPath, String fileName) throws JAXBException {
File xmlFile = new File(directoryPath + "/" + fileName);
displayMappingContent(unmarshallMenu(xmlFile));
}
public void displayMappingContent(Menu menu) {
System.out.println(renderMenu(menu));
}
public String renderMenu(Menu menu) {
AtomicInteger count = new AtomicInteger(1);
List<Content> contents = menu.getContents();
StringBuffer sb = new StringBuffer();
sb.append("Type : " + menu.getType().toUpperCase());
contents.stream()
.flatMap(content -> content.getFoods().stream()).toList()
.forEach(food -> sb.append("NR. " + count.getAndIncrement()
+ "\nName : " + food.getName() + "\nPrice : " + food.getPrice()
+ "\nDescription : " + food.getDescription() + "\nCalories" + food.getCalories() + "\n"));
return sb.toString()
}
public Menu unmarshallMenu(File xmlFile) {
JAXBContext jaxbContext = JAXBContext.newInstance(Menu.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
Menu menu = (Menu) jaxbUnmarshaller.unmarshal(xmlFile);
}
}
Now the original two methods are trivial, and the new ones are simple to test.
You could also split your service into three: a #Component to unmarshall, a #Component to render menus, and the existing service which is a client of those components. Then when you test the service you can mock those components.

Jackson error: Cannot deserialize instance of `java.lang.String` out of START_ARRAY token

Hey I have also problem here is my Json
[
{
"aimid": "12345"
},
{
"aimid": "333674"
},
{
"aimid": [
"4568999",
"6789345"
]
}]
and This is my Pojo class:-
#JsonProperty("aimid")
private String aimid;
public String getAimid() {
return aimid;
}
public void setAimid(String aimid) {
this.aimid = aimid;
}
I want to store aimid in pojo . When i am writing like above in my application i am getting error.
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.lang.String` out of START_ARRAY token.
From my understanding i am getting error because of Array element so anyone can suggest me how i can capture both thing if it is coming as String or It is coming as a Array String
The challenge is that in some cases "aimid" is a string value but in another case it is an array.
If you have control over the structure of the JSON then update the structure so that each element of the root array has ONE of the following structures:
String
{
"aimid": "333674"
}
OR array
{
"aimid": [
"4568999",
"6789345"
]
}
If you do not have control of the structure of the data you will need to parse it yourself and process it into your POJO.
Please see these 3 code examples that should illustrate how you can go about this approaches. :
public class MyPojo {
private List<String> aimid;
#JsonProperty("aimid")
public List<String> getAimid() {
return aimid;
}
#JsonProperty("aimid_array")
public void setAimid(final List<String> aimid) {
this.aimid = aimid;
}
#JsonProperty("aimid")
public void setAimid(final String aimid) {
this.aimid = Arrays.asList(aimid);
}
}
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.node.*;
import java.io.IOException;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.junit.Test;
public class UnitTest {
private static final Logger LOGGER = Logger.getLogger(UnitTest.class.getName());
public UnitTest() {
}
#Test
public void testOneAimId() throws IOException {
final String json = "[\n"
+ "{\n"
+ " \"aimid\": \"12345\"\n"
+ "},\n"
+ "{\n"
+ " \"aimid\": \"333674\"\n"
+ "}]";
final List<MyPojo> result = new ObjectMapper().readValue(json, new TypeReference<List<MyPojo>>() {
});
log(Level.SEVERE, LOGGER, "testOneAimId", result);
}
#Test
public void testListAimIds() throws IOException {
final String json = "[\n"
+ "{\n"
+ " \"aimid_array\": [\n" // HERE WE HAVE CHANGED THE JSON PROP NAME
+ " \"4568999\",\n"
+ " \"6789345\"\n"
+ " ]\n"
+ "}]";
final List<MyPojo> result = new ObjectMapper().readValue(json, new TypeReference<List<MyPojo>>() {
});
log(Level.SEVERE, LOGGER, "testListAimIds", result);
}
#Test
public void testMixed() throws IOException {
final String json = "[\n"
+ "{\n"
+ " \"aimid\": \"12345\"\n"
+ "},\n"
+ "{\n"
+ " \"aimid\": \"333674\"\n"
+ "},\n"
+ "{\n"
+ " \"aimid_array\": [\n" // HERE WE HAVE CHANGED THE JSON PROP NAME
+ " \"4568999\",\n"
+ " \"6789345\"\n"
+ " ]\n"
+ "}]";
final List<MyPojo> result = new ObjectMapper().readValue(json, new TypeReference<List<MyPojo>>() {
});
log(Level.SEVERE, LOGGER, "testMixed", result);
}
#Test
public void testMixed2() throws IOException {
final String json = "[\n"
+ "{\n"
+ " \"aimid\": \"12345\"\n"
+ "},\n"
+ "{\n"
+ " \"aimid\": \"333674\"\n"
+ "},\n"
+ "{\n"
+ " \"aimid\": [\n"
+ " \"4568999\",\n"
+ " \"6789345\"\n"
+ " ]\n"
+ "}]";
final JsonNode result = new ObjectMapper().readValue(json, JsonNode.class);
final ArrayList<String> arrayList = new ArrayList<>();
result.forEach((final JsonNode jsonNode) -> {
if (jsonNode.getNodeType() != JsonNodeType.OBJECT)
throw new IllegalArgumentException(jsonNode.toString());
final ObjectNode obj = (ObjectNode) jsonNode;
obj.forEach(o -> {
switch (o.getNodeType()) {
case ARRAY:
final ArrayNode array = (ArrayNode) o;
array.forEach(t -> arrayList.add(t.asText()));
break;
case STRING:
arrayList.add(o.asText());
break;
default:
throw new IllegalArgumentException(o.toString());
}
});
});
final MyPojo myPojo = new MyPojo();
myPojo.setAimid(arrayList);
log(Level.SEVERE, LOGGER, "myPojo", myPojo);
}
private void log(final Level level, final Logger logger, final String title, final Object obj) {
try {
if (title != null)
logger.log(level, title);
final ObjectWriter writer = new ObjectMapper().writerWithDefaultPrettyPrinter();
logger.log(level, obj == null ? "null" : writer.writeValueAsString(obj));
} catch (final JsonProcessingException ex) {
logger.log(Level.SEVERE, ex.getMessage(), ex);
}
}
}

Query a JSON file with Java-Large file

I am trying to parse below JSON file using java.
I need to be able to
search the file by id or name or any of the fields in the object.
search for empty values in the field as well.
The search should return entire object.
The File will be huge and the search should still be time efficient.
[
{
"id": 1,
"name": "Mark Robb",
"last_login": "2013-01-21T05:13:41 -11:30",
"email": "markrobb#gmail.com",
"phone": "12345",
"locations": [
"Germany",
"Austria"
]
},
{
"id": 2,
"name": "Matt Nish",
"last_login": "2014-02-21T07:10:41 -11:30",
"email": "mattnish#gmail.com",
"phone": "456123",
"locations": [
"France",
"Italy"
]
}
]
This is what I have tried so far using Jackson library.
public void findById(int id) {
List<Customer> customers = objectMapper.readValue(new File("src/main/resources/customers.json"), new TypeReference<List<Customer>>(){});
for(Customer customer: customers) {
if(customer.getId() == id) {
System.out.println(customer.getName());
}
}
}
I just don't think this is an efficient method for a huge JSON file(About 20000 customers in a file). And there could be multiple files. Search time should not increase linearly.
How can I make this time efficient? Should I use any other library?
The most efficient (both CPU and memory) way to parse is to use stream oriented parsing instead of object mapping. Usually, it takes a bit more code to be written, but also usually it is a good deal :) Both Gson and Jackson support such lightweight technique. Also, you should avoid memory allocation in the main/hot path to prevent GC pauses. To illustrate the idea I use a small GC-free library https://github.com/anatolygudkov/green-jelly:
import org.green.jelly.*;
import java.io.CharArrayReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.List;
public class SelectById {
public static class Customer {
private long id;
private String name;
private String email;
public void clear() {
id = 0;
name = null;
email = null;
}
public Customer makeCopy() {
Customer result = new Customer();
result.id = id;
result.name = name;
result.email = email;
return result;
}
#Override
public String toString() {
return "Customer{" +
"id=" + id +
", name='" + name + '\'' +
", email='" + email + '\'' +
'}';
}
}
public static void main(String[] args) throws Exception {
final String file = "\n" +
"[\n" +
" {\n" +
" \"id\": 1,\n" +
" \"name\": \"Mark Robb\",\n" +
" \"last_login\": \"2013-01-21T05:13:41 -11:30\",\n" +
" \"email\": \"markrobb#gmail.com\",\n" +
" \"phone\": \"12345\",\n" +
" \"locations\": [\n" +
" \"Germany\",\n" +
" \"Austria\"\n" +
" ]\n" +
"},\n" +
" {\n" +
" \"id\": 2,\n" +
" \"name\": \"Matt Nish\",\n" +
" \"last_login\": \"2014-02-21T07:10:41 -11:30\",\n" +
" \"email\": \"mattnish#gmail.com\",\n" +
" \"phone\": \"456123\",\n" +
" \"locations\": [\n" +
" \"France\",\n" +
" \"Italy\"\n" +
" ]\n" +
" }\n" +
"]\n";
final List<Customer> selection = new ArrayList<>();
final long selectionId = 2;
final JsonParser parser = new JsonParser().setListener(
new JsonParserListenerAdaptor() {
private final Customer customer = new Customer();
private String currentField;
#Override
public boolean onObjectStarted() {
customer.clear();
return true;
}
#Override
public boolean onObjectMember(final CharSequence name) {
currentField = name.toString();
return true;
}
#Override
public boolean onStringValue(final CharSequence data) {
switch (currentField) {
case "name":
customer.name = data.toString();
break;
case "email":
customer.email = data.toString();
break;
}
return true;
}
#Override
public boolean onNumberValue(final JsonNumber number) {
if ("id".equals(currentField)) {
customer.id = number.mantissa();
}
return true;
}
#Override
public boolean onObjectEnded() {
if (customer.id == selectionId) {
selection.add(customer.makeCopy());
return false; // we don't need to continue
}
return true;
}
}
);
// now let's read and parse the data with a buffer
final CharArrayCharSequence buffer = new CharArrayCharSequence(1024);
try (final Reader reader = new CharArrayReader(file.toCharArray())) { // replace by FileReader, for example
int len;
while((len = reader.read(buffer.getChars())) != -1) {
buffer.setLength(len);
parser.parse(buffer);
}
}
parser.eoj();
System.out.println(selection);
}
}
It should work almost as fast as possible in Java (in case we cannot use SIMD instructions directly). To get rid of memory allocation at all (and GC pauses) in the main path, you have to replace ".toString()" (it creates new instance of String) by something reusable like StringBuilder.
The last thing which may affects overall performance is method of the file reading. And RandomAccessFile is one of the best options we have in Java. Since your encoding seems to be ASCII, just cast byte to char to pass to the JsonParser.
It should be possible to do this with Jackson. The trick is to use JsonParser to stream/parse the top-level array and then parse each record using ObjectMapper.readValue().
ObjectMapper objectMapper = new ObjectMapper();
File file = new File("customers.json");
try (JsonParser parser = objectMapper.getFactory().createParser(file))
{
//Assuming top-level array
if (parser.nextToken() != JsonToken.START_ARRAY)
throw new RuntimeException("Expected top-level array in JSON.");
//Now inside the array, parse each record
while (parser.nextToken() != JsonToken.END_ARRAY)
{
Customer customer = objectMapper.readValue(parser, Customer.class);
//Do something with each customer as it is parsed
System.out.println(customer.id + ": " + customer.name);
}
}
#JsonIgnoreProperties(ignoreUnknown = true)
public static class Customer
{
public String id;
public String name;
public String email;
}
In terms of time efficiency it will need to still scan the entire file - not much you can do about that without an index or something fancier like parallel parsing. But it will be more memory efficient than reading the entire JSON into memory - this code only loads one Customer object at a time.
Also:
if(customer.getId() == id) {
Use .equals() for comparing strings, not ==:
if (customer.getId().equals(id)) {
You can try the Gson library. This library implements a TypeAdapter class that converts Java objects to and from JSON by streaming serialization and deserialization.
The API is efficient and flexible especially for huge files. Here is an example:
public class GsonStream {
public static void main(String[] args) {
Gson gson = new Gson();
try (Reader reader = new FileReader("src/main/resources/customers.json")) {
Type listType = new TypeToken<List<Customer>>(){}.getType();
// Convert JSON File to Java Object
List<Customer> customers = gson.fromJson(reader, listType);
List<Customer> names = customers
.stream()
.filter(c -> c.getId() == id)
.map(Customer::getName)
.collect(Collectors.toList());
} catch (IOException e) {
e.printStackTrace();
}
}
}
If you want to understand how to Override the TypeAdapter abstract class here you have and example:
public class GsonTypeAdapter {
public static void main(String args[]) {
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(Customer.class, new customerAdapter());
builder.setPrettyPrinting();
Gson gson = builder.create();
try {
reader = new JsonReader(new FileReader("src/main/resources/customers.json"));
Customer customer = gson.fromJson(jsonString, Customer.class);
System.out.println(customer);
jsonString = gson.toJson(customer);
System.out.println(jsonString);
} catch (IOException e) {
e.printStackTrace();
}
}
}
class customerAdapter extends TypeAdapter<Customer> {
#Override
public customer read(JsonReader reader) throws IOException {
Customer customer = new customer();
reader.beginObject();
String fieldName = null;
while (reader.hasNext()) {
JsonToken token = reader.peek();
if (token.equals(JsonToken.NAME)) {
//get the current token
fieldName = reader.nextName();
}
if ("name".equals(fieldName)) {
//move to next token
token = reader.peek();
customer.setName(reader.nextString());
}
if("id".equals(fieldName)) {
//move to next token
token = reader.peek();
customer.setRollNo(reader.nextInt());
}
}
reader.endObject();
return customer;
}
#Override
public void write(JsonWriter writer, Customer customer) throws IOException {
writer.beginObject();
writer.name("name");
writer.value(customer.getName());
writer.name("id");
writer.value(customer.getId());
writer.endObject();
}
}
class Customer {
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String toString() {
return "Customer[ name = " + name + ", id: " + id + "]";
}
}

JSON formatting for a Java Server

I am trying to read JSON string using gson into a Java program. In the sample code below - the Java program has 3 object classes. The data in the json string will have a variable number of object instances of each class. I have tried to create a sample JSON - to parse .. but had problems parsing the various objects.
Is this the right way to consume a json string or can it be done in a different way.. How would you parse a json with variable objects of different classes. Thanks,
package newpackage;
import java.util.ArrayList;
import com.google.gson.Gson;
public class jsonsample {
public static void main(String[] args) {
String jsonstring = "{'TableA':[{'field_A1':'A_11'},{'field_A1':'A_12'}]}"
+ ",{'TableB':[{'field_B1':'B_11','field_B2':'B_12','field_B3':['abc','def','ghi']},"
+ "{'field_B1':'B_21','field_B2':'B_Field22','field_B3':['mno','pqr','xyz']}]"
+ ",{'TableC':[{'field_C1':'C_11','field_C2':'C_12','field_C3':'C_13'},"
+ "{'field_C1':'C_21','field_C2':'C_22','field_C3':'C_23'},{'field_C1':'C_31','field_C2':'C_32','field_C3':'C_33'}]}";
jsonstring = jsonstring.replace('\'', '"');
}
public class TableA {
String field_A1;
public TableA(String a){
this.field_A1 = a;
}
#Override
public String toString() {
return ("Table A" + " " + this.field_A1);
}
}
public class TableB {
String field_B1;
String field_B2;
ArrayList<String> field_B3 = new ArrayList<String>();
public TableB(String a, String b, ArrayList<String> c){
this.field_B1 = a;
this.field_B2 = b;
this.field_B3 = c;
}
#Override
public String toString() {
return ("Table B" + " " + this.field_B1+ " " + this.field_B2);
}
}
public class TableC {
String field_C1;
String field_C2;
String field_C3;
public TableC(String a, String b, String c){
this.field_C1 = a;
this.field_C2 = b;
this.field_C3 = c;
}
#Override
public String toString() {
return ("Table C" + " " + this.field_C1 + " " + this.field_C2 + " " + this.field_C3);
}
}
}
First of all you have to decide what is your base json structure ? Max identifiers, max values, max objects,max arrays...
Create your full json structure with texteditor or http://www.jsoneditoronline.org/ or http://jsonlint.com/ etc.
Let's think this is my full json structure:
{
"array": [
1,
2,
3
],
"boolean": true,
"null": null,
"number": 123,
"object": {
"a": "b",
"c": "d",
"e": "f"
},
"string": "Hello World"
}
Create your Java Classes as like as your json identifiers. You can use http://json2csharp.com/ convert to Java.
And these are my Java Classes:
public class Object
{
public string a { get; set; }
public string c { get; set; }
public string e { get; set; }
}
public class RootObject
{
public ArrayList<int> array { get; set; }
public Boolean boolean { get; set; }
public Object #null { get; set; }
public int number { get; set; }
public Object #object { get; set; }
public string #string { get; set; }
}
Create your DAO for convert these to structure to them.
For Java;
String data = "jsonString";
RootObject root = new GsonBuilder().create().fromJson(data, RootObject.class);
For Json;
Gson gson = new GsonBuilder().setDateFormat("dd/MM/yyyy").create();
String json = gson.toJson(obj);
Your JSON-string seems incorrect to me. Let me propose the following:
public static void main(String args[]) {
String jsonstring = "["
+ "{'TableA':[{'field_A1':'A_11'},{'field_A1':'A_12'}]}"
+ ",{'TableB':[{'field_B1':'B_11','field_B2':'B_12','field_B3':['abc','def','ghi']},"
+ "{'field_B1':'B_21','field_B2':'B_Field22','field_B3':['mno','pqr','xyz']}]}"
+ ",{'TableC':[{'field_C1':'C_11','field_C2':'C_12','field_C3':'C_13'},"
+ "{'field_C1':'C_21','field_C2':'C_22','field_C3':'C_23'},{'field_C1':'C_31','field_C2':'C_32','field_C3':'C_33'}]}"
+ "]";
jsonstring = jsonstring.replace('\'', '"');
Gson gson = new Gson();
JsonParser parser = new JsonParser();
JsonArray array = parser.parse(jsonstring).getAsJsonArray();
for (JsonElement jsonElement : array) {
JsonObject jsonObject = jsonElement.getAsJsonObject();
Map.Entry<String,JsonElement> table = jsonObject.entrySet().iterator().next();
String tableName = table.getKey();
JsonElement rows = table.getValue();
try {
Class<?> rowClass = Class.forName("[Lnewpackage." + tableName + ";"); // explanation see below this code snippet
// rowClass is an array class!
Object[] parsedRows = gson.fromJson(rows, rowClass);
// do something with parsedRows
for (Object x : parsedRows) {
System.out.println(x);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
Assuming a "table definition" consists of a property named as the class ob the objects in the table, with the objects as array value of that property.
Explanation of Class.forName("[Lnewpackage." + tableName + ";")
This retrieves the Class instance for the array type of a class located in the package newpackage, e.g. newpackage.TableA[] (note the []). Class.forName("A") returns the instance representing the class A. Class.forName("[LA;") returns the instance representing the "class" of an array of As. Using it as a parameter for fromJson(...) it results in the parsing of a JSON array of A-objects.
This is the code - that works based on #hurricane suggestion.
package newpackage;
import java.util.List;
import com.google.gson.*;
public class jsonsample {
public static void main(String[] args) throws ClassNotFoundException {
String jsonstring = "{'TableA':["
+ "{'field_A1':'A_11'},"
+ "{'field_A1':'A_12'}"
+ "],"
+ "'TableB':["
+ "{'field_B1':'B_11','field_B2':'B_12','field_B3':['abc','def']},"
+ "{'field_B1':'B_21','field_B2':'B_22','field_B3':['mno','xyz']}"
+ "],"
+ "'TableC':["
+ "{'field_C1':'C_11','field_C2':'C_12','field_C3':'C_13'},"
+ "{'field_C1':'C_21','field_C2':'C_22','field_C3':'C_23'}"
+ "]}";
jsonstring = jsonstring.replace('\'', '"');
RootObject root = new GsonBuilder().create().fromJson(jsonstring, RootObject.class);
for (int i=0; i < root.TableA.size(); i++){
System.out.println(root.TableA.get(i));
}
for (int i=0; i < root.TableB.size(); i++){
System.out.println(root.TableB.get(i));
}
for (int i=0; i < root.TableC.size(); i++){
System.out.println(root.TableC.get(i));
}
}
public class TableA
{
public String field_A1;
#Override
public String toString() {
return ("Table A" + " " + this.field_A1);
}
}
public class TableB{
public String field_B1;
public String field_B2;
public List<String> field_B3;
#Override
public String toString() {
return ("Table B" + " " + this.field_B1 + " " + this.field_B2 + " " + this.field_B3);
}
}
public class TableC{
public String field_C1;
public String field_C2;
public String field_C3;
#Override
public String toString() {
return ("Table C" + " " + this.field_C1 + " " + this.field_C2 + " " + this.field_C3);
}
}
public class RootObject{
public List<TableA> TableA;
public List<TableB> TableB;
public List<TableC> TableC;
}
}
The output for the above is:
Table A A_11
Table A A_12
Table B B_11 B_12 [abc, def]
Table B B_21 B_22 [mno, xyz]
Table C C_11 C_12 C_13
Table C C_21 C_22 C_23

getting data from xml to java

How could I get the data from a xml, which is generated with php, in java.
I need the data to display it in a listview in my android app.
The phpcode take the data form the mysqlquery and fetch the array in the variabel xml and put it over echo out. The data for the mysqlquery are from the android app over POST.
phpcode:
//MySQL zugangsdaten
$server = "server";
$datenbank = "database";
$username = "username";
$passwort = "password";
//Verbindung zur MySqldatenbank herstellen
$link = mysql_connect($server, $username, $passwort);
if (!$link) die(mysql_error());
//Datenbank auswählen
$db = mysql_select_db($datenbank, $link);
//<---- End Login ---->
$_linie = htmlspecialchars(mysql_real_escape_string($_POST["linie"]), ENT_COMPAT);
$_richtung = htmlspecialchars(mysql_real_escape_string($_POST["richtung"]), ENT_COMPAT);
$sql_befehl = "SELECT * From Kontrolleure where linie = '$_linie' AND richtung = '$_richtung'";
$query = mysql_query($sql_befehl, $link);
if(mysql_error())
{
die(mysql_error());
}
while($result = mysql_fetch_array($query, MYSQL_ASSOC))
{
$count = $count + 1;
$xml = $xml."<Konduktor>";
$xml = $xml."<id>".$result['id']."</id>";
$xml = $xml."<linie>".$result['linie']."</linie>";
$xml = $xml."<endstation>".$result['richtung']."</endstation>";
$xml = $xml."<station>".$result['station']."</station>";
$xml = $xml."<zeit>".$result['zeit']."</zeit>";
$xml = $xml."</Konduktor>";
}
echo "<Konduktors count=\"$count\">";
echo $xml;
echo "</Konduktors>";
the xml response looks like this:
<Konduktors count="3">
<Konduktor>
<id>29</id>
<linie>S23</linie>
<endstation>Langenthal</endstation>
<station>Brugg AG</station>
<zeit>17:36:34</zeit>
</Konduktor>
<Konduktor>
<id>30</id>
<linie>S23</linie>
<endstation>Langenthal</endstation>
<station>Lupfig</station>
<zeit>17:37:12</zeit>
</Konduktor>
<Konduktor>
<id>32</id>
<linie>S23</linie>
<endstation>Langenthal</endstation>
<station>Birr</station>
<zeit>16:23:30</zeit>
</Konduktor>
</Konduktors>
Thank you!
There are xml parsing tools for this kind of thing. At my work we use XMLBeans:
http://xmlbeans.apache.org/
I found using it to be fairly straightforward, and am kind of a rank amateur at this kind of stuff.
JAXB would handle that easily:
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
public class JaxbExample {
public static void main(String[] args) throws JAXBException {
String xml =
"<Konduktors count=\"3\">\n" +
" <Konduktor>\n" +
" <id>29</id>\n" +
" <linie>S23</linie>\n" +
" <endstation>Langenthal</endstation>\n" +
" <station>Brugg AG</station>\n" +
" <zeit>17:36:34</zeit>\n" +
" </Konduktor>\n" +
" <Konduktor>\n" +
" <id>30</id>\n" +
" <linie>S23</linie>\n" +
" <endstation>Langenthal</endstation>\n" +
" <station>Lupfig</station>\n" +
" <zeit>17:37:12</zeit>\n" +
" </Konduktor>\n" +
" <Konduktor>\n" +
" <id>32</id>\n" +
" <linie>S23</linie>\n" +
" <endstation>Langenthal</endstation>\n" +
" <station>Birr</station>\n" +
" <zeit>16:23:30</zeit>\n" +
" </Konduktor>\n" +
"</Konduktors>";
Object object = JAXBContext.newInstance(Konduktors.class).createUnmarshaller().unmarshal(new StringReader(xml));
System.out.println(object);
}
#XmlRootElement(name = "Konduktors")
static class Konduktors {
private List<Konductor> konductors = new ArrayList<Konductor>();
#XmlElement(name = "Konduktor")
public List<Konductor> getKonductors() {
return konductors;
}
public void setKonductors(List<Konductor> konductors) {
this.konductors = konductors;
}
#Override
public String toString() {
return "Konductors{" +
"konductors=" + konductors +
'}';
}
}
static class Konductor {
private int id;
private String linie;
private String endstation;
private String zeit;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getLinie() {
return linie;
}
public void setLinie(String linie) {
this.linie = linie;
}
public String getEndstation() {
return endstation;
}
public void setEndstation(String endstation) {
this.endstation = endstation;
}
public String getZeit() {
return zeit;
}
public void setZeit(String zeit) {
this.zeit = zeit;
}
#Override
public String toString() {
return "Konductor{" +
"id=" + id +
", linie='" + linie + '\'' +
", endstation='" + endstation + '\'' +
", zeit='" + zeit + '\'' +
'}';
}
}
}
Other options include XStream or XMLBeans for higher-level abstractions and dom4j or JDOM for lower-level ones--you have to do more work with these but have a lot more flexibility.

Categories