hive querying records for a specific uniontype - hive

I have a sample hive table created as
CREATE TABLE union_test(foo UNIONTYPE<int, double, array<string>, struct<a:int,b:string>>);
The data can be viewed as
SELECT foo FROM union_test;
The output is
{0:1}
{1:2.0}
{2:["three","four"]}
{3:{"a":5,"b":"five"}}
{2:["six","seven"]}
{3:{"a":8,"b":"eight"}}
{0:9}
{1:10.0}
the first field (tag) denotes the type of the union ( 0 for int, 1 for double, 2 for array etc).
My problem is if I found to select only those records where the union type is 2 (array), how should I frame my query?

There is no function in Hive to read data from UnionType. So i wrote 2 UDF´s. One to get Union tag (that you trying to do) and second to get struct from union as an example.
get_union_tag() function:
package HiveUDF;
import org.apache.hadoop.hive.ql.exec.Description;
import org.apache.hadoop.hive.ql.exec.UDFArgumentException;
import org.apache.hadoop.hive.ql.exec.UDFArgumentLengthException;
import org.apache.hadoop.hive.ql.exec.UDFArgumentTypeException;
import org.apache.hadoop.hive.ql.metadata.HiveException;
import org.apache.hadoop.hive.ql.udf.generic.GenericUDF;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.UnionObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory;
#Description(name = "get_union_tag", value = "_FUNC_(unionObject)"
+ " - Returns union object Tag", extended = "Example:\n" + " > SELECT _FUNC_(unionObject) FROM src LIMIT 1;\n one")
public class GetUnionTag extends GenericUDF {
// Global variables that inspect the input.
// These are set up during the initialize() call, and are then used during the
// calls to evaluate()
private transient UnionObjectInspector uoi;
#Override
// This is what we do in the initialize() method:
// Verify that the input is of the type expected
// Set up the ObjectInspectors for the input in global variables
// Return the ObjectInspector for the output
public ObjectInspector initialize(ObjectInspector[] arguments) throws UDFArgumentException {
// Verify the input is of the required type.
// Set the global variables (the various ObjectInspectors) while we're doing this
// Exactly one input argument
if( arguments.length != 1 ){
throw new UDFArgumentLengthException("_FUNC_(unionObject) accepts exactly one argument.");
}
// Is the input an array<>
if( arguments[0].getCategory() != ObjectInspector.Category.UNION ){
throw new UDFArgumentTypeException(0,"The single argument to AddExternalIdToPurchaseDetails should be "
+ "Union<>"
+ " but " + arguments[0].getTypeName() + " is found");
}
// Store the ObjectInspectors for use later in the evaluate() method
uoi = ((UnionObjectInspector)arguments[0]);
// Set up the object inspector for the output, and return it
return PrimitiveObjectInspectorFactory.javaByteObjectInspector;
}
#Override
public Object evaluate(DeferredObject[] arguments) throws HiveException {
byte tag = uoi.getTag(arguments[0].get());
return tag;
}
#Override
public String getDisplayString(String[] children) {
StringBuilder sb = new StringBuilder();
sb.append("get_union_tag(");
for (int i = 0; i < children.length; i++) {
if (i > 0) {
sb.append(',');
}
sb.append(children[i]);
}
sb.append(')');
return sb.toString();
}
}
function get_struct_from_union() UDF :
package HiveUDF;
import org.apache.hadoop.hive.ql.exec.Description;
import org.apache.hadoop.hive.ql.exec.UDFArgumentException;
import org.apache.hadoop.hive.ql.exec.UDFArgumentLengthException;
import org.apache.hadoop.hive.ql.exec.UDFArgumentTypeException;
import org.apache.hadoop.hive.ql.metadata.HiveException;
import org.apache.hadoop.hive.ql.udf.generic.GenericUDF;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.StructObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.UnionObjectInspector;
#Description(name = "get_union_struct", value = "_FUNC_(unionObject)"
+ " - Returns struct ", extended = "Example:\n" + " > _FUNC_(unionObject).value \n 90.0121")
public class GetUnionStruct extends GenericUDF {
// Global variables that inspect the input.
// These are set up during the initialize() call, and are then used during the
// calls to evaluate()
//
// ObjectInspector for the list (input array<>)
// ObjectInspector for the struct<>
// ObjectInspectors for the elements of the struct<>, target, quantity and price
private UnionObjectInspector unionObjectInspector;
private StructObjectInspector structObjectInspector;
#Override
// This is what we do in the initialize() method:
// Verify that the input is of the type expected
// Set up the ObjectInspectors for the input in global variables
// Return the ObjectInspector for the output
public ObjectInspector initialize(ObjectInspector[] arguments) throws UDFArgumentException {
// Verify the input is of the required type.
// Set the global variables (the various ObjectInspectors) while we're doing this
// Exactly one input argument
if( arguments.length != 1 ){
throw new UDFArgumentLengthException("_FUNC_(unionObject) accepts exactly one argument.");
}
// Is the input an array<>
if( arguments[0].getCategory() != ObjectInspector.Category.UNION ){
throw new UDFArgumentTypeException(0,"The single argument to AddExternalIdToPurchaseDetails should be "
+ "Union<Struct>"
+ " but " + arguments[0].getTypeName() + " is found");
}
// Set up the object inspector for the output, and return it
return structObjectInspector;
}
#Override
public Object evaluate(DeferredObject[] arguments) throws HiveException {
return ((UnionObjectInspector) unionObjectInspector).getField(arguments[0].get());
}
#Override
public String getDisplayString(String[] children) {
StringBuilder sb = new StringBuilder();
sb.append("get_union_vqtstruct(");
for (int i = 0; i < children.length; i++) {
if (i > 0) {
sb.append(',');
}
sb.append(children[i]);
}
sb.append(')');
return sb.toString();
}
}
to use these UDF´s compile and create jar file. Than upload into hive (in my case HDInsight). Than just use
add jar wasb:///hive/HiveGUDF.jar;
CREATE TEMPORARY FUNCTION get_union_struct AS 'HiveUDF.GetUnionStruct';
before u run e.g.
SELECT get_union_tag(exposed) FROM test;

Related

Pass Type into function in Kotlin

In Swift you can pass a type into a function like so:
func foo<T>(_ type: T.Type)
foo(String.self)
Is there any equivalent in Kotlin?
I don't want to specify the type in the generic arguments, because there are multiple generic arguments and it would be annoying to have to specify them all when the compiler can work it out itself except for this one. Also I don't want to use KClass as that requires the type to by Any which I don't want to make a requirement for the type.
you can you class.java.typeName
/**
* Return an informative string for the name of this type.
*
* #return an informative string for the name of this type
* #since 1.8
*/
public String getTypeName() {
if (isArray()) {
try {
Class<?> cl = this;
int dimensions = 0;
do {
dimensions++;
cl = cl.getComponentType();
} while (cl.isArray());
StringBuilder sb = new StringBuilder();
sb.append(cl.getName());
for (int i = 0; i < dimensions; i++) {
sb.append("[]");
}
return sb.toString();
} catch (Throwable e) { /*FALLTHRU*/ }
}
return getName();
}
foo(Int::class.java.typeName)
foo(String::class.java.typeName)

SQL query into JTable

I've found one totally working query, which gets columns and their data from Oracle DB and puts the output in console printout.
I've spent 3 hours trying to display this data in Swing JTable.
When I am trying to bind data with JTable:
jTable1.setModel(new javax.swing.table.DefaultTableModel(
data, header
));
it keeps telling me that constructor is invalid. That's true, because I need arrays [] and [][] to make that. Any ideas how this can be implemented?
Here is the original query:
package com.javacoderanch.example.sql;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
public class MetadataColumnExample {
private static final String DRIVER = "oracle.jdbc.OracleDriver";
private static final String URL = "jdbc:oracle:thin:#//XXX";
private static final String USERNAME = "XXX";
private static final String PASSWORD = "XXX";
public static void main(String[] args) throws Exception {
Connection connection = null;
try {
//
// As the usual ritual, load the driver class and get connection
// from database.
//
Class.forName(DRIVER);
connection = DriverManager.getConnection(URL, USERNAME, PASSWORD);
//
// In the statement below we'll select all records from users table
// and then try to find all the columns it has.
//
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery("select *\n"
+ "from booking\n"
+ "where TRACKING_NUMBER = 1000001741");
//
// The ResultSetMetaData is where all metadata related information
// for a result set is stored.
//
ResultSetMetaData metadata = resultSet.getMetaData();
int columnCount = metadata.getColumnCount();
//
// To get the column names we do a loop for a number of column count
// returned above. And please remember a JDBC operation is 1-indexed
// so every index begin from 1 not 0 as in array.
//
ArrayList<String> columns = new ArrayList<String>();
for (int i = 1; i < columnCount; i++) {
String columnName = metadata.getColumnName(i);
columns.add(columnName);
}
//
// Later we use the collected column names to get the value of the
// column it self.
//
while (resultSet.next()) {
for (String columnName : columns) {
String value = resultSet.getString(columnName);
System.out.println(columnName + " = " + value);
}
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
connection.close();
}
}
}
Ok I found a bit better way adding SQL query to JTable without even using the ArrayList. Maybe will be helpful for someone, completely working:
import java.awt.*;
import javax.swing.*;
import java.sql.*;
import java.awt.image.BufferedImage;
public class report extends JFrame {
PreparedStatement ps;
Connection con;
ResultSet rs;
Statement st;
JLabel l1;
String bn;
int bid;
Date d1, d2;
int rows = 0;
Object data1[][];
JScrollPane scroller;
JTable table;
public report() {
Container cp = getContentPane();
cp.setLayout(new BorderLayout());
setSize(600, 600);
setLocation(50, 50);
setLayout(new BorderLayout());
setTitle("Library Report");
try {
Class.forName("oracle.jdbc.OracleDriver");
con = DriverManager.getConnection("jdbc:oracle:thin:#XXXXX", "XXXXX", "XXXXX");
} catch (Exception e) {
}
try {
/*
* JDBC 2.0 provides a way to retrieve a rowcount from a ResultSet without having to scan through all the rows
* So we add TYPE_SCROLL_INSENSITIVE & CONCUR_READ_ONLY
*/
st = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); //Creating Statement Object
} catch (SQLException sqlex) {
System.out.println("!!!###");
}
try {
rs = st.executeQuery("select TRACKING_NUMBER, INCO_TERM_CODE, MODE_OF_TRANSPORT\n"
+ "from salog.booking\n"
+ "where rownum < 5");
// Counting rows
rs.last();
int rows = rs.getRow();
rs.beforeFirst();
System.out.println("cc " + rows);
ResultSetMetaData metaData = rs.getMetaData();
int colummm = metaData.getColumnCount();
System.out.println("colms =" + colummm);
Object[] Colheads = {"BookId", "BookName", "rtyry"};
if (Colheads.length != colummm) {
// System.out.println("EPT!!");
JOptionPane.showMessageDialog(rootPane, "Incorrect Column Headers quantity listed in array! The program will now exit.", "System Error", JOptionPane.ERROR_MESSAGE);
System.exit(0);
}
data1 = new Object[rows][Colheads.length];
for (int i1 = 0; i1 < rows; i1++) {
rs.next();
for (int j1 = 0; j1 < Colheads.length; j1++) {
data1[i1][j1] = rs.getString(j1 + 1);
}
}
JTable table = new JTable(data1, Colheads);
JScrollPane jsp = new JScrollPane(table);
getContentPane().add(jsp);
} catch (Exception e) {
}
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String args[]) {
JFrame frm = new report();
frm.setSize(600, 600);
frm.setLocation(50, 50);
BufferedImage image = null;
frm.setIconImage(image);
frm.setVisible(true);
frm.show();
}
}

get and set methods from an ArrayList in another class

/*
* Notes here
*/
package billboardsign;
/**
*
* #author John Parada
*/
import java.util.ArrayList;
public class Billboard
{
private String text;
private ArrayList<String> messages = new ArrayList<String>();
public Billboard()
{
messages.add("Happy New Year");
messages.add("Happy Holidays");
messages.add("Team Pizza");
messages.add("Game On");
messages.add("Let's Go Team");
}
public ArrayList<String> getMessages()
{
return messages;
}
public void setMessages(String msg)
{
messages.add(msg);
}
public boolean isEmpty()
{
return text == null || text.isEmpty();
}
public String substring(int begin, int end)
{
if (begin >= 0 && end < text.length())
{
return text.substring(begin, end);
}
else
return null;
}
//add the method Reverse here
// THIS CODE IS NO GOOD!for (int count = text.length() - 1; count >= 0; count-- )
//FOLLOW UP LINE System.out.printf( "%s ", text.charAt( count ) );
public String reverse()
{
if (isEmpty())
return null;
else
{
char[] chars = text.toCharArray();
//create antoher arrayList
char[] reverse = new char[chars.length];
for (int i = chars.length - 1, j = 0; i < 0; i--, j++)
{
reverse[j] = chars[i];
}
return new String(reverse);
}
}
//add the method Replace string here
public String replace(char oldChar, char newChar)
{
return text.replace(oldChar, newChar);
}
//add the method displayInfo here
public void displayInfo()
{
System.out.printf("\n%s\nMessage", messages);
}
}
/*
* Notes on the Billboard Project
*/
package billboardsign;
/**
*
* #author John Parada
*/
import java.util.Scanner;
public class BillboardSign
{
/**
* #param args the command line arguments
*/
public static void main(String[] args)
{
Billboard newBillboard = new Billboard(); //default constructor
//Menu
int choose_again = 1;
int choiceNumber;
Scanner newInput = new Scanner(System.in);
while (choose_again > 0 )
{
//list options to user
System.out.println ("\nPlease choose a number between 1 and 6 to choose an\n"
+ "option for the Messages Board to display: "
+ "\n1) Display Default Message 1"
+ "\n2) Display Default Message 2"
+ "\n3) Display Default Message 3"
+ "\n4) Display Default Message 4"
+ "\n5) Display Default Message 5"
+ "\n6) Enter a New Message"
+ "\n7) Reverse a Message"
+ "\n8) Replace a Message - Substring"
+ "\n9) Exit Program");
//get the user to input a selection
System.out.print ("\nPlease Enter Your Selection: ");
choiceNumber = newInput.nextInt();
//use switch statement to help with thieir choice input
switch (choiceNumber)
{
case 1:
//execute get() and displayInfo() method for default Message 1
//newBillboard = Billboard();
messages.get(0);
displayInfo();
break;
case 2:
//execute get() and displayInfo() method for default Message 2
newBillboard = Billboard();
Billboard = displayInfo();
break;
case 3:
//execute get() and displayInfo() method for default Message 3
newBillboard = Billboard();
Billboard = displayInfo();
break;
case 4:
//execute get() and displayInfo() method for default Message 4
newBillboard = Billboard();
Billboard = displayInfo();
break;
case 5:
displayInfo();
break;
case 6:
//execute set() and displayInfo() methods to create a new message
break;
case 7:
//execute reverse message method
break;
case 8:
//execute Replace message - Substring method
case 9:
//Dispaly to the user that they have chose to exit
System.out.print ("\nYou have chosen to cancel and exit. \n");
System.exit(0);
}
//prompt user for antother selection? (Contiune=1 and Exit= -1)
System.out.println();
System.out.print ("\nIf you would like to select another animal enter 1 or if your are done enter -1: ");
choose_again = newInput.nextInt();
}
}
private static void displayInfo() {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
}
In short, when the user selects 1, he or she is supposed to get the first element of the ArrayList(0). When he or she selects 2, they will get the second element in the ArrayList(1). I need to invoke the get method, and use the displayInfo of the Billboard.
Thank you in advance for any and all help.
Regards,
John Parada
do something likewise,
case 1:
newBillboard.getMessages().get(0);
...
case 1:
newBillboard.getMessages().get(1);
....
And so on.
your getMessages() method is returning an arraylist so either you should have in an arraylist variable in your main class like this
ArrayList<String> messages = newBillboard.getMessages();
and then call it like this
case 1:
//execute get() and displayInfo() method for default Message 1
//newBillboard = Billboard();
System.out.println (messages.get(0));
//displayInfo();
break;
or you can simply call it like this
newBillboard.getMessages.get(0);//here 0 is for first element
and there is no need to get an object of your class in every case
and what is the need of displayInfo is unclear so if you tell why have you added that i might see to it.

Create a Gson TypeAdapter for a Guava Range

I am trying to serialize Guava Range objects to JSON using Gson, however the default serialization fails, and I'm unsure how to correctly implement a TypeAdapter for this generic type.
Gson gson = new Gson();
Range<Integer> range = Range.closed(10, 20);
String json = gson.toJson(range);
System.out.println(json);
Range<Integer> range2 = gson.fromJson(json,
new TypeToken<Range<Integer>>(){}.getType());
System.out.println(range2);
assertEquals(range2, range);
This fails like so:
{"lowerBound":{"endpoint":10},"upperBound":{"endpoint":20}}
PASSED: typeTokenInterface
FAILED: range
java.lang.RuntimeException: Unable to invoke no-args constructor for
com.google.common.collect.Cut<java.lang.Integer>. Register an
InstanceCreator with Gson for this type may fix this problem.
at com.google.gson.internal.ConstructorConstructor$12.construct(
ConstructorConstructor.java:210)
...
Note that the default serialization actually loses information - it fails to report whether the endpoints are open or closed. I would prefer to see it serialized similar to its toString(), e.g. [10‥20] however simply calling toString() won't work with generic Range instances, as the elements of the range may not be primitives (Joda-Time LocalDate instances, for example). For the same reason, implementing a custom TypeAdapter seems difficult, as we don't know how to deserialize the endpoints.
I've implemented most of a TypeAdaptorFactory based on the template provided for Multimap which ought to work, but now I'm stuck on the generics. Here's what I have so far:
public class RangeTypeAdapterFactory implements TypeAdapterFactory {
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
Type type = typeToken.getType();
if (typeToken.getRawType() != Range.class
|| !(type instanceof ParameterizedType)) {
return null;
}
Type elementType = ((ParameterizedType) type).getActualTypeArguments()[0];
TypeAdapter<?> elementAdapter = (TypeAdapter<?>)gson.getAdapter(TypeToken.get(elementType));
// Bound mismatch: The generic method newRangeAdapter(TypeAdapter<E>) of type
// GsonUtils.RangeTypeAdapterFactory is not applicable for the arguments
// (TypeAdapter<capture#4-of ?>). The inferred type capture#4-of ? is not a valid
// substitute for the bounded parameter <E extends Comparable<?>>
return (TypeAdapter<T>) newRangeAdapter(elementAdapter);
}
private <E extends Comparable<?>> TypeAdapter<Range<E>> newRangeAdapter(final TypeAdapter<E> elementAdapter) {
return new TypeAdapter<Range<E>>() {
#Override
public void write(JsonWriter out, Range<E> value) throws IOException {
if (value == null) {
out.nullValue();
return;
}
String repr = (value.lowerBoundType() == BoundType.CLOSED ? "[" : "(") +
(value.hasLowerBound() ? elementAdapter.toJson(value.lowerEndpoint()) : "-\u221e") +
'\u2025' +
(value.hasLowerBound() ? elementAdapter.toJson(value.upperEndpoint()) : "+\u221e") +
(value.upperBoundType() == BoundType.CLOSED ? "]" : ")");
out.value(repr);
}
public Range<E> read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
String[] endpoints = in.nextString().split("\u2025");
E lower = elementAdapter.fromJson(endpoints[0].substring(1));
E upper = elementAdapter.fromJson(endpoints[1].substring(0,endpoints[1].length()-1));
return Range.range(lower, endpoints[0].charAt(0) == '[' ? BoundType.CLOSED : BoundType.OPEN,
upper, endpoints[1].charAt(endpoints[1].length()-1) == '[' ? BoundType.CLOSED : BoundType.OPEN);
}
};
}
}
However the return (TypeAdapter<T>) newRangeAdapter(elementAdapter); line has a compilation error and I'm now at a loss.
What's the best way to resolve this error? Is there a better way to serialize Range objects that I'm missing? What about if I want to serialize RangeSets?
Rather frustrating that the Google utility library and Google serialization library seem to require so much glue to work together :(
This feels somewhat like reinventing the wheel, but it was a lot quicker to put together and test than the time spent trying to get Gson to behave, so at least presently I'll be using the following Converters to serialize Range and RangeSet*, rather than Gson.
/**
* Converter between Range instances and Strings, essentially a custom serializer.
* Ideally we'd let Gson or Guava do this for us, but presently this is cleaner.
*/
public static <T extends Comparable<? super T>> Converter<Range<T>, String> rangeConverter(final Converter<T, String> elementConverter) {
final String NEG_INFINITY = "-\u221e";
final String POS_INFINITY = "+\u221e";
final String DOTDOT = "\u2025";
return new Converter<Range<T>, String>() {
#Override
protected String doForward(Range<T> range) {
return (range.hasLowerBound() && range.lowerBoundType() == BoundType.CLOSED ? "[" : "(") +
(range.hasLowerBound() ? elementConverter.convert(range.lowerEndpoint()) : NEG_INFINITY) +
DOTDOT +
(range.hasUpperBound() ? elementConverter.convert(range.upperEndpoint()) : POS_INFINITY) +
(range.hasUpperBound() && range.upperBoundType() == BoundType.CLOSED ? "]" : ")");
}
#Override
protected Range<T> doBackward(String range) {
String[] endpoints = range.split(DOTDOT);
Range<T> ret = Range.all();
if(!endpoints[0].substring(1).equals(NEG_INFINITY)) {
T lower = elementConverter.reverse().convert(endpoints[0].substring(1));
ret = ret.intersection(Range.downTo(lower, endpoints[0].charAt(0) == '[' ? BoundType.CLOSED : BoundType.OPEN));
}
if(!endpoints[1].substring(0,endpoints[1].length()-1).equals(POS_INFINITY)) {
T upper = elementConverter.reverse().convert(endpoints[1].substring(0,endpoints[1].length()-1));
ret = ret.intersection(Range.upTo(upper, endpoints[1].charAt(endpoints[1].length()-1) == ']' ? BoundType.CLOSED : BoundType.OPEN));
}
return ret;
}
};
}
/**
* Converter between RangeSet instances and Strings, essentially a custom serializer.
* Ideally we'd let Gson or Guava do this for us, but presently this is cleaner.
*/
public static <T extends Comparable<? super T>> Converter<RangeSet<T>, String> rangeSetConverter(final Converter<T, String> elementConverter) {
return new Converter<RangeSet<T>, String>() {
private final Converter<Range<T>, String> rangeConverter = rangeConverter(elementConverter);
#Override
protected String doForward(RangeSet<T> rs) {
ArrayList<String> ls = new ArrayList<>();
for(Range<T> range : rs.asRanges()) {
ls.add(rangeConverter.convert(range));
}
return Joiner.on(", ").join(ls);
}
#Override
protected RangeSet<T> doBackward(String rs) {
Iterable<String> parts = Splitter.on(",").trimResults().split(rs);
ImmutableRangeSet.Builder<T> build = ImmutableRangeSet.builder();
for(String range : parts) {
build.add(rangeConverter.reverse().convert(range));
}
return build.build();
}
};
}
*For inter-process communication, Java serialization would likely work just fine, as both classes implement Serializable. However I'm serializing to disk for more permanent storage, meaning I need a format I can trust won't change over time. Guava's serialization doesn't provide that guarantee.
Here is a Gson JsonSerializer and JsonDeserializer that generically supports a Range: https://github.com/jamespedwards42/Fava/wiki/Range-Marshaller
#Override
public JsonElement serialize(final Range src, final Type typeOfSrc, final JsonSerializationContext context) {
final JsonObject jsonObject = new JsonObject();
if ( src.hasLowerBound() ) {
jsonObject.add( "lowerBoundType", context.serialize( src.lowerBoundType() ) );
jsonObject.add( "lowerBound", context.serialize( src.lowerEndpoint() ) );
} else
jsonObject.add( "lowerBoundType", context.serialize( BoundType.OPEN ) );
if ( src.hasUpperBound() ) {
jsonObject.add( "upperBoundType", context.serialize( src.upperBoundType() ) );
jsonObject.add( "upperBound", context.serialize( src.upperEndpoint() ) );
} else
jsonObject.add( "upperBoundType", context.serialize( BoundType.OPEN ) );
return jsonObject;
}
#Override
public Range<? extends Comparable<?>> deserialize(final JsonElement json, final Type typeOfT, final JsonDeserializationContext context) throws JsonParseException {
if ( !( typeOfT instanceof ParameterizedType ) )
throw new IllegalStateException( "typeOfT must be a parameterized Range." );
final JsonObject jsonObject = json.getAsJsonObject();
final JsonElement lowerBoundTypeJsonElement = jsonObject.get( "lowerBoundType" );
final JsonElement upperBoundTypeJsonElement = jsonObject.get( "upperBoundType" );
if ( lowerBoundTypeJsonElement == null || upperBoundTypeJsonElement == null )
throw new IllegalStateException( "Range " + json
+ "was not serialized with this serializer! The default serialization does not store the boundary types, therfore we can not deserialize." );
final Type type = ( ( ParameterizedType ) typeOfT ).getActualTypeArguments()[0];
final BoundType lowerBoundType = context.deserialize( lowerBoundTypeJsonElement, BoundType.class );
final JsonElement lowerBoundJsonElement = jsonObject.get( "lowerBound" );
final Comparable<?> lowerBound = lowerBoundJsonElement == null ? null : context.deserialize( lowerBoundJsonElement, type );
final BoundType upperBoundType = context.deserialize( upperBoundTypeJsonElement, BoundType.class );
final JsonElement upperBoundJsonElement = jsonObject.get( "upperBound" );
final Comparable<?> upperBound = upperBoundJsonElement == null ? null : context.deserialize( upperBoundJsonElement, type );
if ( lowerBound == null && upperBound != null )
return Range.upTo( upperBound, upperBoundType );
else if ( lowerBound != null && upperBound == null )
return Range.downTo( lowerBound, lowerBoundType );
else if ( lowerBound == null && upperBound == null )
return Range.all();
return Range.range( lowerBound, lowerBoundType, upperBound, upperBoundType );
}
Here is a straight forward solution. Works very well
import com.google.common.collect.BoundType;
import com.google.common.collect.Range;
import com.google.gson.*;
import java.lang.reflect.Type;
public class GoogleRangeAdapter implements JsonSerializer, JsonDeserializer {
public static String TK_hasLowerBound = "hasLowerBound";
public static String TK_hasUpperBound = "hasUpperBound";
public static String TK_lowerBoundType = "lowerBoundType";
public static String TK_upperBoundType = "upperBoundType";
public static String TK_lowerBound = "lowerBound";
public static String TK_upperBound = "upperBound";
#Override
public Object deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
JsonObject jsonObject = (JsonObject)json;
boolean hasLowerBound = jsonObject.get(TK_hasLowerBound).getAsBoolean();
boolean hasUpperBound = jsonObject.get(TK_hasUpperBound).getAsBoolean();
if (!hasLowerBound && !hasUpperBound) {
return Range.all();
}
else if (!hasLowerBound && hasUpperBound){
double upperBound = jsonObject.get(TK_upperBound).getAsDouble();
BoundType upperBoundType = BoundType.valueOf(jsonObject.get(TK_upperBoundType).getAsString());
if (upperBoundType == BoundType.OPEN)
return Range.lessThan(upperBound);
else
return Range.atMost(upperBound);
}
else if (hasLowerBound && !hasUpperBound){
double lowerBound = jsonObject.get(TK_lowerBound).getAsDouble();
BoundType lowerBoundType = BoundType.valueOf(jsonObject.get(TK_lowerBoundType).getAsString());
if (lowerBoundType == BoundType.OPEN)
return Range.greaterThan(lowerBound);
else
return Range.atLeast(lowerBound);
}
else {
double lowerBound = jsonObject.get(TK_lowerBound).getAsDouble();
double upperBound = jsonObject.get(TK_upperBound).getAsDouble();
BoundType upperBoundType = BoundType.valueOf(jsonObject.get(TK_upperBoundType).getAsString());
BoundType lowerBoundType = BoundType.valueOf(jsonObject.get(TK_lowerBoundType).getAsString());
if (lowerBoundType == BoundType.OPEN && upperBoundType == BoundType.OPEN)
return Range.open(lowerBound, upperBound);
else if (lowerBoundType == BoundType.OPEN && upperBoundType == BoundType.CLOSED)
return Range.openClosed(lowerBound, upperBound);
else if (lowerBoundType == BoundType.CLOSED && upperBoundType == BoundType.OPEN)
return Range.closedOpen(lowerBound, upperBound);
else
return Range.closed(lowerBound, upperBound);
}
}
#Override
public JsonElement serialize(Object src, Type typeOfSrc, JsonSerializationContext context) {
JsonObject jsonObject = new JsonObject();
Range<Double> range = (Range<Double>)src;
boolean hasLowerBound = range.hasLowerBound();
boolean hasUpperBound = range.hasUpperBound();
jsonObject.addProperty(TK_hasLowerBound, hasLowerBound);
jsonObject.addProperty(TK_hasUpperBound, hasUpperBound);
if (hasLowerBound) {
jsonObject.addProperty(TK_lowerBound, range.lowerEndpoint());
jsonObject.addProperty(TK_lowerBoundType, range.lowerBoundType().name());
}
if (hasUpperBound) {
jsonObject.addProperty(TK_upperBound, range.upperEndpoint());
jsonObject.addProperty(TK_upperBoundType, range.upperBoundType().name());
}
return jsonObject;
}
}

How do I invoke WriteJson recursively?

I use Json.Net.
When I serialize a Department2 object and WriteJson() is invoked I want it to be recursively invoked with each of the Telephone2 objects like I do in ReadJson().
How do I do that?
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
public interface ISimpleDatabag
{
string Databag { get; set; }
}
[JsonConverter(typeof(JsonDataBagCreationConverter<Department2>))]
public class Department2
{
public Telephone2[] Phones { get; set; }
}
[JsonConverter(typeof(JsonDataBagCreationConverter<Telephone2>))]
public class Telephone2
{
public string Name { get; set; }
public string AreaCode { get; set; }
public string Number { get; set; }
}
public class JsonDataBagCreationConverter<T> : JsonConverter where T : new()
{
// Json.Net version 4.5.7.15008
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
// When I serialize Department and this function is invoked
// I want it to recursively invoke WriteJson with each of the Telephone objects
// Like I do in ReadJson
// How do I do that?
T t = (T)value;
serializer.Serialize(writer, t.GetType());
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var jsonObject = JObject.Load(reader);
var target = Create(objectType, jsonObject);
serializer.Populate(jsonObject.CreateReader(), target); // Will call this function recursively for any objects that have JsonDataBagCreationConverter as attribute
return target;
}
protected T Create(Type objectType, JObject jsonObject)
{
return new T();
}
public override bool CanConvert(Type objectType)
{
return typeof(T).IsAssignableFrom(objectType);
}
}
private void Form1_Load(object sender, EventArgs e)
{
string jsonInput = "{\"Name\": \"Seek4\" , \"CustomDepartmentData\": \"This is custom department data\", \"Phones\":[ {\"Name\": \"A\", \"AreaCode\":444, \"Number\":11111111} ,{\"Name\": \"B\", \"AreaCode\":555, \"Number\":987987987}, {\"Name\": \"C\", \"AreaCode\":222, \"Number\":123123123, \"CustomPhoneData\": \"This is custom phone data\"} ] }";
Department2 objDepartment2 = JsonConvert.DeserializeObject<Department2>(jsonInput); // Yes, it works well
Array.Reverse(objDepartment2.Phones);
string jsonNoDatabag = JsonConvert.SerializeObject(objDepartment2);
}
I ended up controlling the entire process myself, using this huge (not refactored) function.
I basically investigate each of the properties of the object to serialize and then serialize it property by property.
Then I can do custom things on each property
/// <summary>
/// Serializes an object by merging its current values into its databag and returns the databag
/// </summary>
/// <param name="objectToSerialize"></param>
/// <returns>the current values merged into the original databag</returns>
/// <remarks>Jan Nielsen, 01-10-2012</remarks>
internal static string SerializeObjectToDatabag(object objectToSerialize)
{
// You have to do it property by property instead of just serializing the entire object and merge it into the original
// because the object might contain lists of objects with custom data and these list might have been sorted differently from when they were loaded
// So you cannot merge them properly unless you do it on a per listitem basis.
// Which is what I do here.
try
{
if (objectToSerialize == null) // If you ie serialize an empty object in an array
{
return null;
}
string updatedDatabag = "";
bool isIDataBag = objectToSerialize is IDataBag;
if (isIDataBag)
{
updatedDatabag = ((IDataBag)objectToSerialize).Data == null ? "" : ((IDataBag)objectToSerialize).Data.ToString();
// updatedDatabag = ((IDataBag)objectToSerialize).Data.ToString(); // Save original data in a local variable. This is the one we will merge new values into
}
string result = "";
// Now iterate through the objects properties
// Possible properties:
// Simple types: string, int, bool etc: their current value should be overwritten in the databag
// types that implement IDatabag: they should be sent to this function recursively so their possible customdata is not overwritten
// but instead their simple values are merged into their own databag. Then the result of this single property merge is overwritten in the outer objects databag
// Types that are not simple and don't implement IDatabag but have properties that implement IDatabag
// types that are not simple and don't implement IDatabag and don't have any properties in any depth that implement IDatabag: They are overwritten in the databag
// Types that are arrays:
// If the types in the array are simple types (string, bool etc) the entire array property is overwritten in the databag
// If the types in the array implement IDatabag each object is sent recursively to this function and their databag is updated via merge
// Then the entire array is overwritten in the outer objects databag
// Types that are generic list are treated like arrays
var properties = objectToSerialize.GetType().GetProperties();
// In order to be able to deserialize abstract classes and interfaces, we need to serialize the classname with the class
// the deserializer recognizes the word $type followed by a type, when its is invoked with a serializerSettings of
// serializerSettings.TypeNameHandling = TypeNameHandling.Objects;
string name = objectToSerialize.GetType().AssemblyQualifiedName;
string shortName = RemoveAssemblyDetails(name);
bool addedType = false;
foreach (var propertyInfo in properties)
{
if (propertyInfo.Name.ToLower() != "data") // Just skip Databag. Databag is not a "real" property but the contents of all the properties when the object was loaded + possible custom data
{
if (!addedType)
{
string jsonSingleProperty = "{ " + ToCustomJson("$type") + " : " + ToCustomJson(shortName) + " }";
// Merge the current value (jsonSingleProperty) into the databag (that might already have been updated with the values of other properties)
// and update the current result with the new values. Ie "Name" : "Seek4" is updated to "Name" : "Seek4Cars" in the databag
// and the function will now use the updated databag to merge the other properties into
updatedDatabag = MergeDefault(jsonSingleProperty, updatedDatabag, true);
addedType = true;
}
// propertyInfo.Name.ToLower().Contains("struct")
var value = propertyInfo.GetValue(objectToSerialize, null); // This gets the value of the specified property in the current object
isIDataBag = value is IDataBag; // Update for the current object. Note that ie an array of IDatabag will return false here, because array is not IsimpleDatabag
// Basically we should just check if the property implements IDatabag
// But the simpletype check is faster because I don't have to check for the interfaces on ie a string, int etc.
// This branch takes care of 3 cases:
// 1) it is a simple type, ie int
// 2) value is null
// 3) it is an array with a value of null
// If an array with values enters this branch of code the values of the array will be appended, overwritten
// Therefore arrays are treated below in a special case. Unless they are null
// GeneralFunctions.IsExtendedSimpleType_AllTypes(propertyInfo.PropertyType) returns true on ie string[], but only arrays with a value of null should be handled here
// This first check originally just checked for simple types
// Then it became extended simple types ie non-simple types that only contains simple types ie List<int,int>
// But not arrays that must be handled separately
// Then it also handled null values
// And then a special case was made for arrays that are null
if ((GeneralFunctions.IsExtendedSimpleType_AllTypes(propertyInfo.PropertyType) || value == null) && (!propertyInfo.PropertyType.IsArray || (propertyInfo.PropertyType.IsArray && value == null)))
{
// You have to merge even though it is default value.
// If you have ie a bool that has an initial value of true and you deliberately sets it to false
// You want the defaultvalue of false to be merged into the json.
string jsonSingleProperty = "{" + ToCustomJson(propertyInfo.Name) + " : " + ToCustomJson(value) + "}"; // ie {"Name" : "Seek4Cars"}
// Merge the current value (jsonSingleProperty) into the databag (that might already have been updated with the values of other properties)
// and update the current result with the new values. Ie "Name" : "Seek4" is updated to "Name" : "Seek4Cars" in the databag
// and the function will now use the updated databag to merge the other properties into
updatedDatabag = MergeDefault(jsonSingleProperty, updatedDatabag, true);
continue;
}
if (isIDataBag) // ie PhoneSingle. A single property of type IDataBag
{
// Invoke recursively
// First check if this is an object with all null values
bool allPropertiesAreNull = true; // Maybe this should in the future be expanded with a check on if the property has its default value ie an int property with a value of 0
foreach (var propertyInfoLocal in value.GetType().GetProperties())
{
var valueLocal = propertyInfoLocal.GetValue(value, null);
if (valueLocal != null)
{
allPropertiesAreNull = false;
break;
}
}
var testjson = "";
if (allPropertiesAreNull)
{
result = "{" + ToCustomJson(propertyInfo.Name) + " : " + " { } }";
}
else
{
testjson = ToCustomJson(value);
result = "{" + ToCustomJson(propertyInfo.Name) + " : " + SerializeObjectToDatabag(value) + "}";
}
updatedDatabag = MergeDefault(result, updatedDatabag, true);
continue;
}
bool containsIDataBag = CheckForDatabagInterfaces.ImplementsInterface(propertyInfo.PropertyType, "idatabag"); // Check if anything inside the property implements IDatabag ie an array of IDatabag
if (containsIDataBag)
{
// Check if it is somekind of generic list (List<T>, Dictionary<T,T) etc) and if it is a type of ignoreTypes ie List<entity>)
if (value.GetType().IsGenericType && value.GetType().GetGenericArguments().Length > 0)
{
string listValuesAsJson = "";
if (value is IEnumerable)
{
listValuesAsJson += "{ " + ToCustomJson(propertyInfo.Name) + " : [";
bool containsItems = false;
foreach (var element in (IEnumerable)value)
{
containsItems = true;
var current = SerializeObjectToDatabag(element);
if (current != null) // If you serialize an empty array element it is null
{
listValuesAsJson += current + ", "; // Add , between each element
}
}
if (containsItems)
{
listValuesAsJson = listValuesAsJson.Substring(0, listValuesAsJson.Length - 2) + "] }"; // remove last , and add ending ] for the array and add a } because this property is flowing in the free
}
else // No items in value
{
listValuesAsJson += "] }"; // add ending ] for the array and add a } because this property is flowing in the free
}
}
else // A single, generic KeyValuePair property
{
listValuesAsJson += "{ " + ToCustomJson(propertyInfo.Name) + " : ";
listValuesAsJson += SerializeObjectToDatabag(value);
listValuesAsJson += " }";
}
updatedDatabag = MergeDefault(listValuesAsJson, updatedDatabag, false);
}
else if (value.GetType().IsArray)
{
string arrayValuesAsJson = "{ " + ToCustomJson(propertyInfo.Name) + " : [";
bool containsItems = false;
foreach (var element in (Array)value)
{
// Treat them the same way you treat any other object
var current = SerializeObjectToDatabag(element);
if (current != null) // If you serialize an empty array element it is null
{
containsItems = true;
arrayValuesAsJson += current + ", ";
}
}
if (containsItems)
{
arrayValuesAsJson = arrayValuesAsJson.Substring(0, arrayValuesAsJson.Length - 2) + "] }"; // remove last , and add ending ] for the array and add a } because this property is flowing in the free
}
else // No items in value
{
arrayValuesAsJson += "] }"; // add ending ] for the array and add a } because this property is flowing in the free
}
updatedDatabag = MergeDefault(arrayValuesAsJson, updatedDatabag, false);
}
else if ( value.GetType().BaseType != null && value.GetType().BaseType.FullName.ToLower().Contains("system.collections.objectmodel"))
{
// This branch was made specifically to take care of the Media collection of a Seek4.Entities.V2.Media.MediaCollection
var genericList = (IList)value;
int counter = genericList.Count;
string listAsJson = "{ " + ToCustomJson(propertyInfo.Name) + " : [";
if (counter == 0)
{
listAsJson += "] }"; // Ie { "Media": [] }
}
else
{
foreach (var obj in genericList)
{
var current = SerializeObjectToDatabag(obj);
listAsJson += current + ", ";
}
listAsJson = listAsJson.Substring(0, listAsJson.Length -2) + " ] }" ;
}
updatedDatabag = MergeDefault(listAsJson, updatedDatabag, true); // hvordan gør json.net dette med standard?
}
else // a single Non-IDatabag property that contains Idatabag properties
{
string tempResult = "{ " + ToCustomJson(propertyInfo.Name) + " : ";
tempResult += SerializeObjectToDatabag(value) + " }";
updatedDatabag = MergeDefault(tempResult, updatedDatabag, true);
}
}
else
{
if (value.GetType().IsArray) // This is an array of simple types so just overwrite
{
string arrayAsJson = "{ " + ToCustomJson(propertyInfo.Name) + " : ";
arrayAsJson += ToCustomJson(value) + "}";
updatedDatabag = MergeDefault(arrayAsJson, updatedDatabag, false);
}
else // ie an object that is not simpledatabag and doesn't contain simple databag
{
string jsonSingleProperty = "{" + ToCustomJson(propertyInfo.Name) + " : " + ToCustomJson(value) + "}";
updatedDatabag = MergeDefault(jsonSingleProperty, updatedDatabag, true);
}
}
}
}
return updatedDatabag;
}
catch (Exception ex)
{
string message = ex.Message;
string stack = ex.StackTrace;
throw;
}
}
internal static string ToCustomJson(object objectToConvertToJson)
{
try
{
// Distinguished from Mongodb.Bson.ToJson() extensionmethod by Custom name
JsonSerializerSettings serializerSettings = new JsonSerializerSettings();
serializerSettings.TypeNameHandling = TypeNameHandling.Objects; // Adds a $type on all objects which we need when it is abstract classes and interfaces
IgnoreDataMemberContractResolver contractResolver = new IgnoreDataMemberContractResolver(null, true, true);
serializerSettings.ContractResolver = contractResolver;
serializerSettings.DefaultValueHandling = DefaultValueHandling.Ignore;
IsoDateTimeConverter converter = new IsoDateTimeConverter();
serializerSettings.Converters.Add(converter);
string result = JsonConvert.SerializeObject(objectToConvertToJson, Formatting.None, serializerSettings);
return result;
}
catch (Exception ex)
{
throw new Exception("Error in ToCustomJson: " + ex.Message, ex);
}
}