/* * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * JSONInstances.java * Copyright (C) 2009-2012 University of Waikato, Hamilton, New Zealand package weka.core.json; import java.util.ArrayList; import weka.core.Attribute; import weka.core.DenseInstance; import weka.core.Instance; import weka.core.Instances; import weka.core.SparseInstance; import weka.core.converters.ConverterUtils.DataSource; *//** * Class for transforming Instances objects into <a href="http://www.json.org/" target="_blank">JSON</a> objects and vice versa. Missing values get stored as "?". * * @author FracPete (fracpete at waikato dot ac dot nz) * @version $Revision: 9664 $ * @see #MISSING_VALUE */ /* public class JSONInstances { *//** the header section. */ /* public final static String HEADER = "header"; *//** the data section. */ /* public final static String DATA = "data"; *//** the relation name. */ /* public final static String RELATION = "relation"; *//** the attributes object. */ /* public final static String ATTRIBUTES = "attributes"; *//** the name attribute. */ /* public final static String NAME = "name"; *//** the type attribute. */ /* public final static String TYPE = "type"; *//** the class attribute indicator. */ /* public final static String CLASS = "class"; *//** the labels attribute. */ /* public final static String LABELS = "labels"; *//** the weight attribute. */ /* public final static String WEIGHT = "weight"; *//** the dateformat attribute. */ /* public final static String DATEFORMAT = "dateformat"; *//** the sparse attribute. */ /* public final static String SPARSE = "sparse"; *//** the values attribute. */ /* public final static String VALUES = "values"; *//** the separator for index/value in case of sparse instances. */ /* public final static String SPARSE_SEPARATOR = ":"; *//** the missing value indicator. */ /* public final static String MISSING_VALUE = "?"; *//** * Turns the JSON object into an Attribute, if possible. * * @param att * the JSON object to turn into an Attribute * @param classAtt * for storing whether the attribute is the class attribute * @return the Attribute, null in case of an error */ /* protected static Attribute toAttribute(JSONNode att, boolean[] classAtt) { Attribute result; String name; String type; String dateformat; JSONNode labels; ArrayList<String> values; String label; int i; double weight; name = (String) att.getChild(NAME).getValue("noname"); type = (String) att.getChild(TYPE).getValue(""); weight = (Double) att.getChild(WEIGHT).getValue(new Double(1.0)); if (type.equals(Attribute.typeToString(Attribute.NUMERIC))) { result = new Attribute(name); } else if (type.equals(Attribute.typeToString(Attribute.NOMINAL))) { labels = att.getChild(LABELS); values = new ArrayList<String>(); for (i = 0; i < labels.getChildCount(); i++) { label = (String) ((JSONNode) labels.getChildAt(i)).getValue(); if (label.equals("'" + MISSING_VALUE + "'")) values.add(MISSING_VALUE); else values.add(label); } result = new Attribute(name, values); } else if (type.equals(Attribute.typeToString(Attribute.DATE))) { dateformat = (String) att.getChild(DATEFORMAT).getValue("yyyy-MM-dd'T'HH:mm:ss"); result = new Attribute(name, dateformat); } else if (type.equals(Attribute.typeToString(Attribute.STRING))) { result = new Attribute(name, (ArrayList<String>) null); } else { System.err.println("Unhandled attribute type '" + type + "'!"); return null; } result.setWeight(weight); return result; } *//** * Turns the JSON Object into an Instance, if possible. * * @param inst * the JSON object to turn into an Instance * @param data * the data so far (only used for header information) * @return the Instance, null in case of an error */ /* protected static Instance toInstance(JSONNode inst, Instances data) { Instance result; boolean sparse; double weight; JSONNode values; int i; int index; int pos; String value; double[] vals; sparse = (Boolean) inst.getChild(SPARSE).getValue(new Boolean(false)); weight = (Double) inst.getChild(WEIGHT).getValue(new Double(1.0)); values = inst.getChild(VALUES); vals = new double[data.numAttributes()]; for (i = 0; i < values.getChildCount(); i++) { if (sparse) { value = "" + ((JSONNode) values.getChildAt(i)).getValue(); pos = value.indexOf(SPARSE_SEPARATOR); index = Integer.parseInt(value.substring(0, pos)); value = value.substring(pos + 1); } else { index = i; value = "" + ((JSONNode) values.getChildAt(i)).getValue(); } try { if (value.equals(MISSING_VALUE)) { vals[index] = Utils.missingValue(); } else { // unescape '?' labels if (value.equals("'" + MISSING_VALUE + "'")) value = MISSING_VALUE; if (data.attribute(index).isNumeric() && !data.attribute(index).isDate()) { vals[index] = Double.parseDouble(value); } else if (data.attribute(index).isNominal()) { vals[index] = data.attribute(index).indexOfValue(value); if ((vals[index] == -1) && value.startsWith("'") && value.endsWith("'")) vals[index] = data.attribute(index).indexOfValue(Utils.unquote(value)); // FIXME backslashes seem to get escaped twice when creating a JSON file? if ((vals[index] == -1) && value.startsWith("'") && value.endsWith("'")) vals[index] = data.attribute(index).indexOfValue(Utils.unbackQuoteChars(Utils.unquote(value))); if (vals[index] == -1) { System.err.println("Unknown label '" + value + "' for attribute #" + (index + 1) + "!"); return null; } } else if (data.attribute(index).isDate()) { vals[index] = data.attribute(index).parseDate(value); } else if (data.attribute(index).isString()) { vals[index] = data.attribute(index).addStringValue(value); } else { System.err.println("Unhandled attribute type '" + Attribute.typeToString(data.attribute(index).type()) + "'!"); return null; } } } catch (Exception e) { System.err.println("Error parsing value #" + (index + 1) + ": " + e.toString()); return null; } } if (sparse) result = new SparseInstance(weight, vals); else result = new DenseInstance(weight, vals); result.setDataset(data); return result; } *//** * Turns a JSON object, if possible, into an Instances object. * * @param json * the JSON object to convert * @param onlyHeader * whether to retrieve only the header * @return the generated Instances object, null if not possible */ /* protected static Instances toInstances(JSONNode json, boolean onlyHeader) { Instances result; JSONNode header; JSONNode attributes; JSONNode data; ArrayList<Attribute> atts; Attribute att; Instance inst; int i; int classIndex; boolean[] classAtt; header = json.getChild(HEADER); if (header == null) { System.err.println("No '" + HEADER + "' section!"); return null; } data = json.getChild(DATA); if (data == null) { System.err.println("No '" + DATA + "' section!"); return null; } // attributes attributes = header.getChild(ATTRIBUTES); if (attributes == null) { System.err.println("No '" + ATTRIBUTES + "' array!"); return null; } atts = new ArrayList<Attribute>(); classAtt = new boolean[1]; classIndex = -1; for (i = 0; i < attributes.getChildCount(); i++) { att = toAttribute((JSONNode) attributes.getChildAt(i), classAtt); if (att == null) { System.err.println("Could not convert attribute #" + (i + 1) + "!"); return null; } if (classAtt[0]) classIndex = i; atts.add(att); } result = new Instances(header.getChild(RELATION).getValue("unknown").toString(), atts, (onlyHeader ? 0 : data.getChildCount())); result.setClassIndex(classIndex); // data if (!onlyHeader) { for (i = 0; i < data.getChildCount(); i++) { inst = toInstance((JSONNode) data.getChildAt(i), result); if (inst == null) { System.err.println("Could not convert instance #" + (i + 1) + "!"); return null; } result.add(inst); } } return result; } *//** * Turns a JSON object, if possible, into an Instances object. * * @param json * the JSON object to convert * @return the generated Instances object, null if not possible */ /* public static Instances toInstances(JSONNode json) { return toInstances(json, false); } *//** * Turns a JSON object, if possible, into an Instances object (only header). * * @param json * the JSON object to convert * @return the generated Instances header object, null if not possible */ /* public static Instances toHeader(JSONNode json) { return toInstances(json, true); } *//** * Turns the Attribute into a JSON object. * * @param inst * the corresponding dataset * @param att * the attribute to convert * @return the JSON object */ /* protected static JSONNode toJSON(Instances inst, Attribute att) { JSONNode result; JSONNode labels; int i; result = new JSONNode(); result.addPrimitive(NAME, att.name()); result.addPrimitive(TYPE, Attribute.typeToString(att)); result.addPrimitive(CLASS, (att.index() == inst.classIndex())); result.addPrimitive(WEIGHT, att.weight()); if (att.isNominal()) { labels = result.addArray(LABELS); for (i = 0; i < att.numValues(); i++) { if (att.value(i).equals(MISSING_VALUE)) labels.addArrayElement("'" + att.value(i) + "'"); else labels.addArrayElement(att.value(i)); } } if (att.isDate()) result.addPrimitive(DATEFORMAT, att.getDateFormat()); return result; } *//** * Turns the Instance into a JSON object. * * @param inst * the Instance to convert * @return the JSON object */ /* protected static JSONNode toJSON(Instance inst) { JSONNode result; JSONNode values; int i; boolean sparse; result = new JSONNode(); sparse = (inst instanceof SparseInstance); result.addPrimitive(SPARSE, sparse); result.addPrimitive(WEIGHT, inst.weight()); values = result.addArray(VALUES); if (sparse) { for (i = 0; i < inst.numValues(); i++) { if (inst.isMissing(inst.index(i))) values.addArrayElement(inst.index(i) + SPARSE_SEPARATOR + MISSING_VALUE); else if (inst.toString(inst.index(i)).equals("'" + MISSING_VALUE + "'")) values.addArrayElement(inst.index(i) + SPARSE_SEPARATOR + "'" + MISSING_VALUE + "'"); // escape '?' labels else values.addArrayElement(inst.index(i) + SPARSE_SEPARATOR + inst.toString(inst.index(i))); } } else { for (i = 0; i < inst.numAttributes(); i++) { if (inst.isMissing(i)) values.addArrayElement(MISSING_VALUE); else if (inst.toString(i).equals("'" + MISSING_VALUE + "'")) values.addArrayElement("'" + MISSING_VALUE + "'"); // escape '?' labels else values.addArrayElement(inst.toString(i)); } } return result; } *//** * Turns the Instances object into a JSON object. * * @param inst * the Instances to turn into a JSON object * @return the JSON object */ /* public static JSONNode toJSON(Instances inst) { JSONNode result; JSONNode header; JSONNode atts; JSONNode data; int i; result = new JSONNode(); // header header = result.addObject(HEADER); header.addPrimitive(RELATION, inst.relationName()); atts = header.addArray(ATTRIBUTES); for (i = 0; i < inst.numAttributes(); i++) atts.add(toJSON(inst, inst.attribute(i))); // data data = result.addArray(DATA); for (i = 0; i < inst.numInstances(); i++) data.add(toJSON(inst.instance(i))); return result; } *//** * For testing only. * * @param args * expects a dataset as first parameter * @throws Exception * if something goes wrong */ /* public static void main(String[] args) throws Exception { if (args.length != 1) { System.err.println("No dataset supplied!"); System.exit(1); } // load dataset Instances data = DataSource.read(args[0]); // turn Instances into JSON object and output it JSONNode json = toJSON(data); StringBuffer buffer = new StringBuffer(); json.toString(buffer); System.out.println(buffer.toString()); // turn JSON object back into Instances and output it Instances inst = toInstances(json); System.out.println(inst); } } */