I have different classes, each receives strings and convert it to different html formats.
Then I have a chooser class where i have switch cases to divert the strings e.g.
i send the strings to chooser and stylename like this
Chooser chooser = new Chooser();
String text = chooser.format(sometext, stylename);
the Chooser class is like this:
public String format(String sometext, String stylename) {
switch (stylename) {
case "NewStyle":
NewStyle ns = new NewStyle();
str = ns.refprocess(sometext);
break;
case "Anotherstyle":
Anotherstyle as = new Anotherstyle();
str = as.refprocess(sometext);
break;
case "Tet_Letters":
Turk_J_Chem tet_letters = new Turk_J_Chem();
str = tet_letters.refprocess(sometext);
break;
}
}
it there any short way? so that when i send the stylename as String, it converts the to Class, make its object and then send the sometext to that class only?
I assume that you have an interface:
static interface Style {
String refprocess(String text);
}
and 3 implemntations:
static class NewStyle implements Style {
#Override
public String refprocess(final String text) {
return "new style of " + text;
}
}
static class Anotherstyle implements Style {
#Override
public String refprocess(final String text) {
return "Another style of " + text;
}
}
static class Turk_J_Chem implements Style {
#Override
public String refprocess(final String text) {
return "Turk_J_Chem's style of " + text;
}
}
Then you can create an enum of classes, and format using the enum:
enum Styles1 {
NEW(NewStyle.class, "NewStyle"),
ANOTHER(Anotherstyle.class, "Anotherstyle"),
TET_LETTERS(Turk_J_Chem.class, "Tet_Letters");
String str;
Class<? extends Style> clazz;
Styles1(final Class<? extends Style> clazz, final String str) {
this.clazz = clazz;
this.str = str;
}
}
static String formatUsingEnum1(final String sometext, final String stylename) {
for (final Styles1 style : Styles1.values()) {
if (style.str.equals(stylename)) {
try {
return style.clazz.newInstance().refprocess(sometext);
} catch (final Exception e) {
break;
}
}
}
throw new NotImplementedException(stylename);
}
Or you can populate the enum with real instances:
enum Styles2 {
NEW(new NewStyle(), "NewStyle"),
ANOTHER(new Anotherstyle(), "Anotherstyle"),
TET_LETTERS(new Turk_J_Chem(), "Tet_Letters");
String str;
Style style;
Styles2(final Style instance, final String str) {
style = instance;
this.str = str;
}
}
static String formatUsingEnum2(final String sometext, final String stylename) {
for (final Styles2 style : Styles2.values()) {
if (style.str.equals(stylename)) {
try {
return style.style.refprocess(sometext);
} catch (final Exception e) {
break;
}
}
}
throw new NotImplementedException(stylename);
}
But you can insert an abstract function into the enum, with inline implementation for all instances:
enum Styles3 {
NewStyle() {
#Override
String refprocess(final String text) {
return "new style of " + text;
}
},
Anotherstyle {
#Override
String refprocess(final String text) {
return "Another style of " + text;
}
},
TET_LETTERS {
#Override
String refprocess(final String text) {
return "Turk_J_Chem's style of " + text;
}
};
abstract String refprocess(String text);
}
static String formatUsingEnum3(final String sometext, final String stylename) {
for (final Styles3 style : Styles3.values()) {
if (style.name().equalsIgnoreCase(stylename)) {
try {
return style.refprocess(sometext);
} catch (final Exception e) {
break;
}
}
}
throw new NotImplementedException(stylename);
}
Enjoy...
Related
Lang: Java
We are trying to copy the list of pojo to the clipboard in the table format.
Bullet point: what we are trying to achieve here :
1. convert list of pojo into table format
2. If the user copy it in some excel sheet then the it should be copied easily or even if the user try to copy in notepad it should print in the table format.
3. Add some meta data to clipboard to determine the pojo when we will import the table again.
For converting the list of pojo to table format i have used the jtable but i am not able to export all the jtable content to clipboard.
can anyone suggest if i should follow the jtable approach and copy the table to clipboard or any other solution is also available.
Update: as suggested in the comment I tried using the flavours
public class ClipboardTest implements ClipboardOwner {
public static void main(String[] args) {
ClipboardTest clipboardTest = new ClipboardTest();
clipboardTest.copyToClipboard();
//clipboardTest.getFromClipboard();
}
public void copyToClipboard() {
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
Pojo data = new Pojo("1", "2", "ame2", "2", "2");
MyObjectSelection dataSelection = new MyObjectSelection(data);
StringSelection selection = new StringSelection("testing string");
clipboard.setContents(dataSelection, ClipboardTest.this);
System.out.println("copied to clipboard");
}
public void getFromClipboard() {
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
Transferable clipboardContent = clipboard.getContents(this);
DataFlavor[] flavors = clipboardContent.getTransferDataFlavors();
System.out.println("flavors.length = " + flavors.length);
for (int i = 0; i < flavors.length; i++) {
System.out.println("flavor[" + i + "] = " + flavors[i]);
}
}
// ClipboardOwner implementation
#Override
public void lostOwnership(Clipboard clipboard, Transferable transferable) {
System.out.println("ClipboardTest: Lost ownership");
}
}
---
myobjectselection.java
public class MyObjectSelection implements Transferable, ClipboardOwner {
private static DataFlavor dmselFlavor = new DataFlavor(Pojo.class,
"Test data flavor");
private Pojo selection;
public MyObjectSelection(Pojo selection) {
this.selection = selection;
}
// Transferable implementation
#Override
public DataFlavor[] getTransferDataFlavors() {
System.out.println("getTransferDataFlavors");
DataFlavor[] ret = { dmselFlavor };
return ret;
}
#Override
public boolean isDataFlavorSupported(DataFlavor flavor) {
return dmselFlavor.equals(flavor);
}
#Override
public synchronized Object getTransferData(DataFlavor flavor)
throws UnsupportedFlavorException {
if (isDataFlavorSupported(flavor)) {
return this.selection;
} else {
throw new UnsupportedFlavorException(dmselFlavor);
}
}
// ClipboardOwner implementation
#Override
public void lostOwnership(Clipboard clipboard, Transferable transferable) {
System.out.println("MyObjectSelection: Lost ownership");
}
}
--
pojo.java
public class Pojo implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
private String name;
private String name1;
private String name2;
private String name3;
private String name4;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getName1() {
return name1;
}
public void setName1(String name1) {
this.name1 = name1;
}
public String getName2() {
return name2;
}
public void setName2(String name2) {
this.name2 = name2;
}
public String getName3() {
return name3;
}
public void setName3(String name3) {
this.name3 = name3;
}
public String getName4() {
return name4;
}
public void setName4(String name4) {
this.name4 = name4;
}
public Pojo(String name, String name1, String name2, String name3,
String name4) {
super();
this.name = name;
this.name1 = name1;
this.name2 = name2;
this.name3 = name3;
this.name4 = name4;
}
}
When i am trying to copy the string value to clipboard it is working but when i am trying to copy the pojo then is it not working.
For every flavor you want to support, you must provide methods to encode the object in the specified format.
This means you'll likely need to provide an encoder for plain text, html and CVS to cover the basics.
So, based on your Pojo, I wrote a Transferable that supports:
List (of Pojos)
HTML
CVS
Plain text
And serialised (as an additional to List, but it's essentially the same)
PojoTransferable
public class PojoTransferable implements Transferable {
public static final DataFlavor POJO_LIST_DATA_FLAVOR = new DataFlavor(List.class, "application/x-java-pojo-list;class=java.util.List");
public static final DataFlavor HTML_DATA_FLAVOR = new DataFlavor("text/html", "HTML");
public static final DataFlavor CSV_DATA_FLAVOR = new DataFlavor("text/csv", "CVS");
public static final DataFlavor PLAIN_DATA_FLAVOR = new DataFlavor("text/plain", "Plain text");
public static final DataFlavor SERIALIZED_DATA_FLAVOR = new DataFlavor(Pojo.class, "application/x-java-serialized-object; Pojo");
private static String[] HEADERS = new String[]{"name", "name1", "name2", "name3", "name4"};
private static Pojo POJO_HEADER = new Pojo("name", "name1", "name2", "name3", "name4");
private List<Pojo> pojos;
public PojoTransferable(List<Pojo> pojos) {
this.pojos = pojos;
}
#Override
public DataFlavor[] getTransferDataFlavors() {
return new DataFlavor[]{POJO_LIST_DATA_FLAVOR, HTML_DATA_FLAVOR, CSV_DATA_FLAVOR, SERIALIZED_DATA_FLAVOR, PLAIN_DATA_FLAVOR};
}
#Override
public boolean isDataFlavorSupported(DataFlavor flavor) {
boolean supported = false;
for (DataFlavor mine : getTransferDataFlavors()) {
if (mine.equals(flavor)) {
supported = true;
break;
}
}
return supported;
}
#Override
public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
Object data = null;
if (POJO_LIST_DATA_FLAVOR.equals(flavor)) {
data = pojos;
} else if (HTML_DATA_FLAVOR.equals(flavor)) {
data = new ByteArrayInputStream(formatAsHTML().getBytes());
} else if (SERIALIZED_DATA_FLAVOR.equals(flavor)) {
data = pojos;
} else if (CSV_DATA_FLAVOR.equals(flavor)) {
data = new ByteArrayInputStream(formatAsCVS().getBytes());
} else if (PLAIN_DATA_FLAVOR.equals(flavor)) {
data = new ByteArrayInputStream(formatAsPlainText().getBytes());
} else {
throw new UnsupportedFlavorException(flavor);
}
return data;
}
protected String formatAsCVS(Pojo pojo) {
StringJoiner sj = new StringJoiner(",");
sj.add(pojo.getName());
sj.add(pojo.getName2());
sj.add(pojo.getName3());
sj.add(pojo.getName4());
return sj.toString();
}
public String formatAsCVS() {
StringBuilder sb = new StringBuilder(128);
sb.append(formatAsCVS(POJO_HEADER));
for (Pojo pojo : pojos) {
sb.append(formatAsCVS(pojo));
}
return "";
}
protected Map<Integer, Integer> columnWidthsFor(Pojo pojo) {
Map<Integer, Integer> columnWidths = new HashMap<>();
columnWidths.put(0, pojo.getName().length());
columnWidths.put(1, pojo.getName1().length());
columnWidths.put(2, pojo.getName2().length());
columnWidths.put(3, pojo.getName3().length());
columnWidths.put(4, pojo.getName4().length());
return columnWidths;
}
protected void apply(Map<Integer, Integer> pojoWidths, Map<Integer, Integer> columnWidths) {
for (int index = 0; index < 5; index++) {
int currentWidth = 2;
if (columnWidths.containsKey(index)) {
currentWidth = columnWidths.get(index);
}
int columnWidth = 2;
if (pojoWidths.containsKey(index)) {
columnWidth = pojoWidths.get(index);
}
columnWidths.put(index, Math.max(currentWidth, columnWidth));
}
}
protected String formatAsPlainText(Pojo pojo, String format) {
return String.format(format, pojo.getName(), pojo.getName1(), pojo.getName2(), pojo.getName3(), pojo.getName4());
}
public static String fill(int padding) {
return String.format("%" + padding + "s", "").replace(" ", "-");
}
public String formatAsPlainText() {
Map<Integer, Integer> columnWidths = new HashMap<>();
apply(columnWidthsFor(POJO_HEADER), columnWidths);
for (Pojo pojo : pojos) {
apply(columnWidthsFor(pojo), columnWidths);
}
StringJoiner sjFormat = new StringJoiner("|");
StringJoiner sjSep = new StringJoiner("+");
for (int index = 0; index < 5; index++) {
int currentWidth = 0;
if (columnWidths.containsKey(index)) {
currentWidth = columnWidths.get(index);
}
sjFormat.add(" %-" + currentWidth + "s ");
sjSep.add(fill(currentWidth + 2));
}
sjFormat.add("%n");
sjSep.add("\n");
String seperator = sjSep.toString();
String format = sjFormat.toString();
StringBuilder sb = new StringBuilder(128);
sb.append(formatAsPlainText(POJO_HEADER, format));
for (Pojo pojo : pojos) {
sb.append(seperator);
sb.append(formatAsPlainText(pojo, format));
}
return sb.toString();
}
public String formatAsHTML() {
StringBuilder sb = new StringBuilder(128);
sb.append("<html><body>");
sb.append("<table border='1'>");
sb.append("<tr>");
for (String header : HEADERS) {
sb.append("<th>").append(header).append("</th>");
}
sb.append("</tr>");
for (Pojo pojo : pojos) {
sb.append("<tr>");
sb.append("<td>").append(pojo.getName()).append("</td>");
sb.append("<td>").append(pojo.getName1()).append("</td>");
sb.append("<td>").append(pojo.getName2()).append("</td>");
sb.append("<td>").append(pojo.getName3()).append("</td>");
sb.append("<td>").append(pojo.getName4()).append("</td>");
sb.append("</tr>");
}
sb.append("</table>");
return sb.toString();
}
}
Test...
Then I wrote a really simple test...
List<Pojo> pojos = new ArrayList<>(25);
pojos.add(new Pojo("one", "two", "three", "four", "five"));
PojoTransferable pt = new PojoTransferable(pojos);
Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
cb.setContents(pt, new ClipboardOwner() {
#Override
public void lostOwnership(Clipboard clipboard, Transferable contents) {
System.out.println("Lost");
}
});
try {
Object data = cb.getData(PojoTransferable.POJO_LIST_DATA_FLAVOR);
if (data instanceof List) {
List listOfPojos = (List)data;
System.out.println("listOfPojos contains " + listOfPojos.size());
for (Object o : listOfPojos) {
System.out.println(o);
}
}
} catch (UnsupportedFlavorException ex) {
Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
} catch (IOException ex) {
Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
}
When I run it, it prints out...
listOfPojos contains 1
test.Pojo#5b480cf9
which tells use that the List flavor worked (for our needs), but then I opened up a text edit, Word and Excel and pasted the contents into them...
nb: Excel used the HTML formatting, go figure
Eclipse can auto-generate a toString() method from a object's fields. If those fields are objects then they too may have similarly auto-generated toString() methods.
e.g. a President object might look like this:
President [country=USA, name=Name [title=Mr, forename=Barack, surname=Obama], address=Address [houseNumber=1600, street=Pennsylvania Avenue, town=Washington]]
which is easier to read if I format it:
President [
country=USA,
name=Name [
title=Mr,
forename=Barack,
surname=Obama],
address=Address [
houseNumber=1600,
street=Pennsylvania Avenue,
town=Washington]]
What is the best way to parse this String to create a map of maps?
I've got a solution, but it's not pretty. I was hoping to be able to avoid the low level String manipulation somehow, but here it is:
import java.util.LinkedHashMap;
import java.util.Map;
public class MappedObject {
public String className;
public Map<String, String> leafFields = new LinkedHashMap<>();
public Map<String, MappedObject> treeFields = new LinkedHashMap<>();
#Override
public String toString() {
return "[className=" + className
+ (leafFields.isEmpty() ? "" : ", leafFields=" + leafFields)
+ (treeFields.isEmpty() ? "" : ", treeFields=" + treeFields)
+ "]";
}
public static MappedObject createFromString(String s) {
MappedObject mo = new MappedObject();
new Mapper(s).mapObject(mo);
return mo;
}
private static class Mapper {
private String s;
public Mapper(String s) {
this.s = s;
}
private String mapObject(MappedObject mo) {
mo.className = removeFirstNCharacters(s.indexOf(' '));
while (s.contains("=")) {
removeLeadingNonLetters();
String key = removeFirstNCharacters(s.indexOf('='));
removeFirstNCharacters(1); // remove the =
String leafValue = getLeafValue();
if (leafValue != null) {
mo.leafFields.put(key, leafValue);
if (s.startsWith("]")) { // that was the last field in the tree
return s;
}
} else {
MappedObject treeField = new MappedObject();
mo.treeFields.put(key, treeField);
s = new Mapper(s).mapObject(treeField);
}
}
return s; // s contains only close brackets - ]
}
private void removeLeadingNonLetters() {
int i = 0;
while (!Character.isLetter(s.charAt(i))) {
i++;
}
removeFirstNCharacters(i);
}
private String removeFirstNCharacters(int n) {
String value = s.substring(0, n);
s = s.substring(value.length());
return value;
}
private String getLeafValue() {
int endIndex = getEndIndex();
if (!s.contains("[") || s.indexOf('[') > endIndex) {
return removeFirstNCharacters(endIndex);
}
return null;
}
/** The end of the value, if it's a leaf field. */
private int getEndIndex() {
if(s.contains(",")) {
return Math.min(s.indexOf(','), s.indexOf(']'));
}
return s.indexOf(']');
}
}
}
I'm trying to make a string formatting mechanism, which pretty much looks like the Winamp Advanced Title Formatting.
I have some 'variables' (or metadata fields) bound to object properties, in the form %varname%. So, for example, the %title% metadata field is bound to a song title, say 'Conquest of Paradise', the %artist% metadata field is bound to the song artist, say 'Vangelis', and the %feat% metadata field is bound to the featuring artists, say 'English Chamber Choir'.
Now I want to display the song depending on a given formatting, for example:
%title%[ (by %artist%[ featuring %feat%])]
Square brackets mean don't display unless (all) metadata inside the brackets were set. Nesting of square brackets should be possible.
So abovementioned formatting string says: display the metadata field %title% and, if %artist% is set (not an empty string), display (by %artist%), but if the %feat% metadata field is also non-empty, then display that field also. In the abovementioned example, it would become:
Conquest of Paradise (by Vangelis featuring English Chamber Choir)
Now how do I make such mechanism? Where do I start?
I guess that I have to tokenize the string and then per 'section' search for metadata tags?
I would build up a tree structure that represents the pattern. For your example, it would look like:
root
+ variable (title)
+ group
+ text (" (by ")
+ variable (artist)
+ group
+ text (" featuring ")
+ variable (feat)
+ text (")")
Then when you evaluate meta data against your tree, you store at the group level whether all variables and sub-groups in the group evaluated, and if so use the text.
Your tree classes would look something like:
interface Node { String evaluate(Map<String, String> metaData); }
class Group implements Node
{
private final List<Node> _children;
Group(final List<Node> children) { _children = children; }
#Override
public String evaluate(final Map<String, String> metaData)
{
final StringBuilder sb = new StringBuilder();
for (final Node node : _children)
{
final String subText = node.evaluate(metaData);
if (subText == null)
return null;
sb.append(subText);
}
return sb.toString();
}
}
class Text implements Node
{
private final String _text;
Text(final String text) { _text = text; }
#Override
public String evaluate(final Map<String, String> metaData)
{
return _text;
}
}
class Variable implements Node
{
private final String _variable;
Variable(final String variable) { _variable = variable; }
#Override
public String evaluate(final Map<String, String> metaData)
{
return metaData.get(_variable);
}
}
All that's left to do is to work out how to parse your string to create the tree structure.
Based on the suggestion of SimonC, I've written a tokenizer what executes what has been suggested, to split the formatting string into tokens.
public class Main {
private static void buildTree(String format) {
Stack<Token> st = new Stack<>();
StringBuilder sb = new StringBuilder();
GroupToken root = new GroupToken();
st.push(root);
boolean var = false;
for (int i = 0; i < format.length(); i++) {
char currentChar = format.charAt(i);
switch (currentChar) {
case '[':
String str = sb.toString();
sb.setLength(0); // Flush the StringBuilder
if (!str.equals("")) {
((GroupToken) st.peek()).add(new TextToken(str));
}
GroupToken gt = new GroupToken();
((GroupToken) st.peek()).add(gt);
st.push(gt);
break;
case ']':
str = sb.toString();
sb.setLength(0); // Flush the StringBuilder
if (!str.equals("")) {
((GroupToken) st.peek()).add(new TextToken(str));
}
st.pop();
break;
case '%':
var = !var;
if (var) {
str = sb.toString();
sb.setLength(0); // Flush the StringBuilder
if (!str.equals("")) {
((GroupToken) st.peek()).add(new TextToken(str));
}
}
else {
str = sb.toString();
sb.setLength(0); // Flush the StringBuilder
((GroupToken) st.peek()).add(new VariableToken(str));
}
break;
default:
sb.append(currentChar);
break;
}
}
// Process the last remains of the string buffer...
String str = sb.toString();
sb.setLength(0); // Flush the StringBuilder
if (!str.equals("")) {
((GroupToken) st.peek()).add(new TextToken(str));
}
st.pop();
System.out.println(root);
}
public static void main(String[] arguments) throws Exception {
buildTree("%title%[ (%alttitle%[, #%track%])]");
}
}
abstract class Token {
public abstract String toString(int indent);
}
class TextToken extends Token {
private String text;
public TextToken(String text) {
this.text = text;
}
#Override
public String toString() {
return toString(0);
}
#Override
public String toString(int indent) {
return "TextToken[\"" + this.text + "\"]\n";
}
}
class VariableToken extends Token {
private String text;
public VariableToken(String text) {
this.text = text;
}
#Override
public String toString() {
return toString(0);
}
#Override
public String toString(int indent) {
return "VariableToken[\"" + this.text + "\"]\n";
}
}
class GroupToken extends Token {
ArrayList<Token> tokens = new ArrayList<>();
public GroupToken() { }
public void add(Token token) {
this.tokens.add(token);
}
#Override
public String toString() {
return toString(0);
}
#Override
public String toString(int indent) {
String out = "GroupToken[\n";
for (Token t : this.tokens) {
out += StringUtils.pad("", 4 * (indent + 1), ' ') + t.toString(indent + 1);
}
out += StringUtils.pad("", 4 * indent, ' ') + "]\n";
return out;
}
}
I parsed my xml file successfully, but now I am getting null value. so what mistake I make in my coding I don't know. I want to display my string value in my screen. Now I am trying to display that string value in text view format, but I am getting null value......
my xml file:
<Mobiles>
<Mobile>
<Phone>Nokia 1108</Phone>
<Network>GSM 900/1800 MHz</Network>
<Size>106x46x20 mm</Size>
<Ringtones>mono</Ringtones>
<SMS>yes</SMS>
<MMS>no</MMS>
<Email>no</Email>
<InstantMessaging >no</InstantMessaging>
</Mobile>
<Mobile>
<Phone>Nokia 1109</Phone>
<Network>GSM 900/1800 MHz</Network>
<Size>106x46x20 mm</Size>
<Ringtones>mono</Ringtones>
<SMS>yes</SMS>
<MMS>no</MMS>
<Email>no</Email>
<InstantMessaging >no</InstantMessaging>
</Mobile>
<Mobile>
<Phone>Nokia 1110</Phone>
<Network>GSM 900/1800 MHz</Network>
<Size>106x46x20 mm</Size>
<Ringtones>mono</Ringtones>
<SMS>yes</SMS>
<MMS>no</MMS>
<Email>no</Email>
<InstantMessaging >no</InstantMessaging>
</Mobile>
<Mobile>
<Phone>Nokia 1111</Phone>
<Network>GSM 900/1800 MHz</Network>
<Size>106x46x20 mm</Size>
<Ringtones>mono</Ringtones>
<SMS>yes</SMS>
<MMS>no</MMS>
<Email>no</Email>
<InstantMessaging >no</InstantMessaging>
</Mobile>
</Mobiles>
output:
Just briefly glancing at your code I'd say that the issue is the state transitions on your in_Mobiles variable. It will always be true from the start of the document to the end.
In your characters(char[], int, int) method, the very first conditional branch will thus consume all characters:
if (this.in_Mobiles) {
myParsedExampleDataSet.setMobiles(new String(ch, start, length));
The same behavior repeats in the use of in_Mobile, which if you fix the first one, will be the next culprit.
Edit:
Well, overall your parser implementation is kind of wonky. Try something like this instead:
First off, your ParsedExampleDataSet is a bit off.
Turn it into a List of Mobile objects instead, like this:
public class ParsedExampleDataSet extends ArrayList<Mobile>{
}
Next, make a bean class named Mobile, like this:
class Mobile {
private String Phone;
private String Network;
private String Size;
private String Ringtones;
private boolean SMS;
private boolean MMS;
private boolean Email;
private boolean InstantMessaging;
public String getPhone() {
return Phone;
}
public void setPhone(String phone) {
Phone = phone;
}
public String getNetwork() {
return Network;
}
public void setNetwork(String network) {
Network = network;
}
public String getSize() {
return Size;
}
public void setSize(String size) {
Size = size;
}
public String getRingtones() {
return Ringtones;
}
public void setRingtones(String ringtones) {
Ringtones = ringtones;
}
public boolean isSMS() {
return SMS;
}
public void setSMS(boolean sMS) {
SMS = sMS;
}
public boolean isMMS() {
return MMS;
}
public void setMMS(boolean mMS) {
MMS = mMS;
}
public boolean isEmail() {
return Email;
}
public void setEmail(boolean email) {
Email = email;
}
public boolean isInstantMessaging() {
return InstantMessaging;
}
public void setInstantMessaging(boolean instantMessaging) {
InstantMessaging = instantMessaging;
}
}
Finally, your DefaultHandler subclass needs to be reworked. Something like this ought to work.
class ExampleHandler extends DefaultHandler {
private ParsedExampleDataSet Mobiles;
private Mobile CurrentMobile;
private StringBuilder Characters;
public ParsedExampleDataSet getParsedExampleDataSet() {
return Mobiles;
}
public void startDocument() throws SAXException {
Mobiles = new ParsedExampleDataSet();
}
public void startElement(String namespaceUri, String localName, String qName, Attributes atts)
throws SAXException {
String name = localName.equals("") ? qName : localName;
if ("Mobile".equals(name)) {
CurrentMobile = new Mobile();
}
// Empty accumulated characters
Characters = null;
}
public void characters(char[] ch, int offset, int length) throws SAXException {
if (Characters == null) {
Characters = new StringBuilder(length);
}
Characters.append(ch, offset, length);
}
public void endElement(String namespaceUri, String localName, String qName) throws SAXException {
String name = localName.equals("") ? qName : localName;
if ("Mobile".equals(name)) {
Mobiles.add(CurrentMobile);
CurrentMobile = null;
} else if (CurrentMobile != null && Characters != null){
String value = Characters.toString();
if ("Phone".equals(name)) {
CurrentMobile.setPhone(value);
} else if ("Network".equals(name)) {
CurrentMobile.setNetwork(value);
} else if ("Size".equals(name)) {
CurrentMobile.setSize(value);
} else if ("Ringtones".equals(name)) {
CurrentMobile.setRingtones(value);
} else {
boolean yes = "yes".equalsIgnoreCase(value.trim());
if ("SMS".equals(name)) {
CurrentMobile.setSMS(yes);
} else if ("MMS".equals(name)) {
CurrentMobile.setMMS(yes);
} else if ("Email".equals(name)) {
CurrentMobile.setEmail(yes);
} else if ("InstantMessaging".equals(name)) {
CurrentMobile.setInstantMessaging(yes);
}
}
}
}
}
And, just running it like this should produce a result:
SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
ExampleHandler handler = new ExampleHandler();
InputSource is = new InputSource(/* your XML goes here as an inputstream or reader*/);
parser.parse(is, handler);
ParsedExampleDataSet mobiles = handler.getParsedExampleDataSet();
for (Mobile mobile : mobiles) {
System.out.println(mobile.getPhone());
}
why do you have true in endElement method?
if (localName.equals("Mobiles")) {
this.in_Mobiles = true;
this could leads to always override mobiles and not setting correct field.
I am working on an Android application, using the EmpireAvenue API.
The API uses JSON and I'm using the GSON library to parse the data from the API.
Here is the problem:
I have a JSON structure like this:
{
type: "earnings",
info: {
earnings: 64.09
dividends: 1277.34
gains: 1997.05
expenses: 4895.51
shares_bought: 210
shares_bought_user_count: 2
shares_sold: 0
shares_sold_user_count: 0
},
created: "2011-04-16 11:32:37"
},
{
type: "following",
info: [
{
ticker: "SOLPHE"
full_name: "Rodrigo Bermudez Salazar"
list_name: "My Recommended Buys"
},
{
ticker: "SOLPHE"
full_name: "Rodrigo Bermudez Salazar"
list_name: "My Watch List"
}
],
created: "2011-04-16 11:00:08"
}
As you can see, the structure associated with the info field is different. Sometimes it's an object, sometimes an array. As expected, the GSON library throws errors when parsing.
Do you know how to parse a JSON structure with when a field changes structure ?
Thanks for your help.
The current solution with Gson is a bit involved, requiring implementation of a custom Instance Creator and/or a custom Deserializer. Take a look at http://code.google.com/p/google-gson/issues/detail?id=231 and the release notes on Hierarchical Type Adapters for details. I just posted an example of polymorphic deserialization with Gson in response to Polymorphism with gson.
Gson hopefully will soon have the RuntimeTypeAdapter for simpler polymorphic deserialization. See http://code.google.com/p/google-gson/issues/detail?id=231 for more info.
On the other hand, a Jackson-based solution isn't so bad.
public class Foo
{
static String jsonInput =
"[" +
"{" +
"\"type\":\"earnings\"," +
"\"info\":" +
"{" +
"\"earnings\":64.09," +
"\"dividends\":1277.34," +
"\"gains\":1997.05," +
"\"expenses\":4895.51," +
"\"shares_bought\":210," +
"\"shares_bought_user_count\":2," +
"\"shares_sold\":0," +
"\"shares_sold_user_count\":0" +
"}," +
"\"created\":\"2011-04-16 11:32:37\"" +
"}," +
"{" +
"\"type\":\"following\"," +
"\"info\":" +
"[" +
"{" +
"\"ticker\":\"SOLPHE\"," +
"\"full_name\":\"RodrigoBermudezSalazar\"," +
"\"list_name\":\"MyRecommendedBuys\"" +
"}," +
"{" +
"\"ticker\":\"SOLPHE\"," +
"\"full_name\":\"RodrigoBermudezSalazar\"," +
"\"list_name\":\"MyWatchList\"" +
"}" +
"]," +
"\"created\":\"2011-04-16 11:00:08\"" +
"}" +
"]";
public static void main(String[] args) throws Exception
{
ObjectMapper mapper = new ObjectMapper();
mapper.setPropertyNamingStrategy(new CamelCaseNamingStrategy());
DateFormat dataFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
mapper.setDateFormat(dataFormat);
Collection<Thing> things = mapper.readValue(jsonInput, new TypeReference<Collection<Thing>>(){});
System.out.println(things);
}
}
#JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="type")
#JsonSubTypes({#Type(value=Earnings.class, name="earnings"), #Type(value=Following.class, name="following")})
abstract class Thing
{
private Date created;
void setCreated(Date created)
{
this.created = created;
}
#Override
public String toString()
{
return String.format(
"[%1$s: created=%2$s, other attributes:%3$s]",
getClass().getSimpleName(), created, toStringAddenda());
}
abstract String toStringAddenda();
}
class Earnings extends Thing
{
private EarningsInfo info;
void setInfo(EarningsInfo info)
{
this.info = info;
}
#Override
String toStringAddenda()
{
return info.toString();
}
}
class Following extends Thing
{
private Collection<FollowingInfo> info;
void setInfo(Collection<FollowingInfo> info)
{
this.info = info;
}
#Override
String toStringAddenda()
{
return info.toString();
}
}
class FollowingInfo
{
private String ticker;
private String fullName;
private String listName;
void setTicker(String ticker)
{
this.ticker = ticker;
}
void setFullName(String fullName)
{
this.fullName = fullName;
}
void setListName(String listName)
{
this.listName = listName;
}
#Override
public String toString()
{
return String.format(
"[FollowingInfo: ticker=%1$s, fullName=%2$s, listName=%3$s]",
ticker, fullName, listName);
}
}
class EarningsInfo
{
private BigDecimal earnings;
private BigDecimal dividends;
private BigDecimal gains;
private BigDecimal expenses;
private int sharesBought;
private int sharesBoughtUserCount;
private int sharesSold;
private int sharesSoldUserCount;
void setEarnings(BigDecimal earnings)
{
this.earnings = earnings;
}
void setDividends(BigDecimal dividends)
{
this.dividends = dividends;
}
void setGains(BigDecimal gains)
{
this.gains = gains;
}
void setExpenses(BigDecimal expenses)
{
this.expenses = expenses;
}
void setSharesBought(int sharesBought)
{
this.sharesBought = sharesBought;
}
void setSharesBoughtUserCount(int sharesBoughtUserCount)
{
this.sharesBoughtUserCount = sharesBoughtUserCount;
}
void setSharesSold(int sharesSold)
{
this.sharesSold = sharesSold;
}
void setSharesSoldUserCount(int sharesSoldUserCount)
{
this.sharesSoldUserCount = sharesSoldUserCount;
}
#Override
public String toString()
{
return String.format(
"[EarningsInfo: earnings=%1$s, dividends=%2$s, gains=%3$s, expenses=%4$s, sharesBought=%5$s, sharesBoughtUserCount=%6$s, sharesSold=%7$s, sharesSoldUserCount=%8$s]",
earnings, dividends, gains, expenses, sharesBought, sharesBoughtUserCount, sharesSold, sharesSoldUserCount);
}
}
class CamelCaseNamingStrategy extends PropertyNamingStrategy
{
#Override
public String nameForGetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName)
{
return convert(defaultName);
}
#Override
public String nameForSetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName)
{
return convert(defaultName);
}
#Override
public String nameForField(MapperConfig<?> config, AnnotatedField field, String defaultName)
{
return convert(defaultName);
}
private String convert(String defaultName)
{
char[] nameChars = defaultName.toCharArray();
StringBuilder nameTranslated = new StringBuilder(nameChars.length * 2);
for (char c : nameChars)
{
if (Character.isUpperCase(c))
{
nameTranslated.append("_");
c = Character.toLowerCase(c);
}
nameTranslated.append(c);
}
return nameTranslated.toString();
}
}