I have written below code to read avro schema records from Kafka topic. I have taken .avsc and generate a class(paymentengine) using maven and reading record with SpecificAvroRecord. I am able to read these records successfully. Now, I need to do some validation on these records and insert these records into a table.
package com.example.consumer;
import io.confluent.kafka.serializers.KafkaAvroDeserializer;
import io.confluent.kafka.serializers.KafkaAvroDeserializerConfig;
import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer;
import org.apache.kafka.common.serialization.StringDeserializer;
import java.time.Duration;
import java.util.Collections;
import java.util.Properties;
public class PayKafkaSpecifcAvro {
public static void main(String[] args) {
// setting properties
Properties props = new Properties();
props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, KafkaAvroDeserializer.class.getName());
props.put(ConsumerConfig.GROUP_ID_CONFIG, "group1");
props.put(KafkaAvroDeserializerConfig.SPECIFIC_AVRO_READER_CONFIG, "true");
props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");
props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "false");
props.put(KafkaAvroDeserializerConfig.SCHEMA_REGISTRY_URL_CONFIG, "http://localhost:8081");
//name topic
String topic = "pengine";
// create the consumer
KafkaConsumer<String, pengine> consumer = new KafkaConsumer<String, pengine>(props);
//subscribe to topic
consumer.subscribe(Collections.singleton(topic));
System.out.println("Waiting for the data...");
while (true) {
ConsumerRecords<String, pengine> records = consumer.poll(Duration.ofMillis(5000));
for (ConsumerRecord<String, pengine> record : records) {
System.out.println(record.value());
System.out.println((record.value().getVcp()));
consumer.commitSync();
}
}
}
}
Output :
As the output is in JSON format, how can I convert it into a String and Compare it? I need to compare ACH and VCP value and if the values are same then have to flag that row as error. Also, converting these records in string would help me to insert these records into database as well.
pengine class:
package com.example.consumer; /**
* Autogenerated by Avro
*
* DO NOT EDIT DIRECTLY
*/
import org.apache.avro.message.BinaryMessageDecoder;
import org.apache.avro.message.BinaryMessageEncoder;
import org.apache.avro.message.SchemaStore;
import org.apache.avro.specific.SpecificData;
import org.apache.avro.util.Utf8;
#org.apache.avro.specific.AvroGenerated
public class pengine extends org.apache.avro.specific.SpecificRecordBase implements org.apache.avro.specific.SpecificRecord {
private static final long serialVersionUID = -3169039590588895557L;
public static final org.apache.avro.Schema SCHEMA$ = new org.apache.avro.Schema.Parser().parse("{\"type\":\"record\",\"name\":\"pengine\",\"fields\":[{\"name\":\"tin\",\"type\":\"string\"},{\"name\":\"ach\",\"type\":\"string\"},{\"name\":\"vcp\",\"type\":\"string\"}]}");
public static org.apache.avro.Schema getClassSchema() { return SCHEMA$; }
private static SpecificData MODEL$ = new SpecificData();
private static final BinaryMessageEncoder<pengine> ENCODER =
new BinaryMessageEncoder<pengine>(MODEL$, SCHEMA$);
private static final BinaryMessageDecoder<pengine> DECODER =
new BinaryMessageDecoder<pengine>(MODEL$, SCHEMA$);
/**
* Return the BinaryMessageEncoder instance used by this class.
* #return the message encoder used by this class
*/
public static BinaryMessageEncoder<pengine> getEncoder() {
return ENCODER;
}
/**
* Return the BinaryMessageDecoder instance used by this class.
* #return the message decoder used by this class
*/
public static BinaryMessageDecoder<pengine> getDecoder() {
return DECODER;
}
/**
* Create a new BinaryMessageDecoder instance for this class that uses the specified {#link SchemaStore}.
* #param resolver a {#link SchemaStore} used to find schemas by fingerprint
* #return a BinaryMessageDecoder instance for this class backed by the given SchemaStore
*/
public static BinaryMessageDecoder<pengine> createDecoder(SchemaStore resolver) {
return new BinaryMessageDecoder<pengine>(MODEL$, SCHEMA$, resolver);
}
/**
* Serializes this pengine to a ByteBuffer.
* #return a buffer holding the serialized data for this instance
* #throws java.io.IOException if this instance could not be serialized
*/
public java.nio.ByteBuffer toByteBuffer() throws java.io.IOException {
return ENCODER.encode(this);
}
/**
* Deserializes a pengine from a ByteBuffer.
* #param b a byte buffer holding serialized data for an instance of this class
* #return a pengine instance decoded from the given buffer
* #throws java.io.IOException if the given bytes could not be deserialized into an instance of this class
*/
public static pengine fromByteBuffer(
java.nio.ByteBuffer b) throws java.io.IOException {
return DECODER.decode(b);
}
private CharSequence tin;
private CharSequence ach;
private CharSequence vcp;
/**
* Default constructor. Note that this does not initialize fields
* to their default values from the schema. If that is desired then
* one should use <code>newBuilder()</code>.
*/
public pengine() {}
/**
* All-args constructor.
* #param tin The new value for tin
* #param ach The new value for ach
* #param vcp The new value for vcp
*/
public pengine(CharSequence tin, CharSequence ach, CharSequence vcp) {
this.tin = tin;
this.ach = ach;
this.vcp = vcp;
}
public SpecificData getSpecificData() { return MODEL$; }
public org.apache.avro.Schema getSchema() { return SCHEMA$; }
// Used by DatumWriter. Applications should not call.
public Object get(int field$) {
switch (field$) {
case 0: return tin;
case 1: return ach;
case 2: return vcp;
default: throw new org.apache.avro.AvroRuntimeException("Bad index");
}
}
// Used by DatumReader. Applications should not call.
#SuppressWarnings(value="unchecked")
public void put(int field$, Object value$) {
switch (field$) {
case 0: tin = (CharSequence)value$; break;
case 1: ach = (CharSequence)value$; break;
case 2: vcp = (CharSequence)value$; break;
default: throw new org.apache.avro.AvroRuntimeException("Bad index");
}
}
/**
* Gets the value of the 'tin' field.
* #return The value of the 'tin' field.
*/
public CharSequence getTin() {
return tin;
}
/**
* Sets the value of the 'tin' field.
* #param value the value to set.
*/
public void setTin(CharSequence value) {
this.tin = value;
}
/**
* Gets the value of the 'ach' field.
* #return The value of the 'ach' field.
*/
public CharSequence getAch() {
return ach;
}
/**
* Sets the value of the 'ach' field.
* #param value the value to set.
*/
public void setAch(CharSequence value) {
this.ach = value;
}
/**
* Gets the value of the 'vcp' field.
* #return The value of the 'vcp' field.
*/
public CharSequence getVcp() {
return vcp;
}
/**
* Sets the value of the 'vcp' field.
* #param value the value to set.
*/
public void setVcp(CharSequence value) {
this.vcp = value;
}
/**
* Creates a new pengine RecordBuilder.
* #return A new pengine RecordBuilder
*/
public static pengine.Builder newBuilder() {
return new pengine.Builder();
}
/**
* Creates a new pengine RecordBuilder by copying an existing Builder.
* #param other The existing builder to copy.
* #return A new pengine RecordBuilder
*/
public static pengine.Builder newBuilder(pengine.Builder other) {
if (other == null) {
return new pengine.Builder();
} else {
return new pengine.Builder(other);
}
}
/**
* Creates a new pengine RecordBuilder by copying an existing pengine instance.
* #param other The existing instance to copy.
* #return A new pengine RecordBuilder
*/
public static pengine.Builder newBuilder(pengine other) {
if (other == null) {
return new pengine.Builder();
} else {
return new pengine.Builder(other);
}
}
/**
* RecordBuilder for pengine instances.
*/
#org.apache.avro.specific.AvroGenerated
public static class Builder extends org.apache.avro.specific.SpecificRecordBuilderBase<pengine>
implements org.apache.avro.data.RecordBuilder<pengine> {
private CharSequence tin;
private CharSequence ach;
private CharSequence vcp;
/** Creates a new Builder */
private Builder() {
super(SCHEMA$);
}
/**
* Creates a Builder by copying an existing Builder.
* #param other The existing Builder to copy.
*/
private Builder(pengine.Builder other) {
super(other);
if (isValidValue(fields()[0], other.tin)) {
this.tin = data().deepCopy(fields()[0].schema(), other.tin);
fieldSetFlags()[0] = other.fieldSetFlags()[0];
}
if (isValidValue(fields()[1], other.ach)) {
this.ach = data().deepCopy(fields()[1].schema(), other.ach);
fieldSetFlags()[1] = other.fieldSetFlags()[1];
}
if (isValidValue(fields()[2], other.vcp)) {
this.vcp = data().deepCopy(fields()[2].schema(), other.vcp);
fieldSetFlags()[2] = other.fieldSetFlags()[2];
}
}
/**
* Creates a Builder by copying an existing pengine instance
* #param other The existing instance to copy.
*/
private Builder(pengine other) {
super(SCHEMA$);
if (isValidValue(fields()[0], other.tin)) {
this.tin = data().deepCopy(fields()[0].schema(), other.tin);
fieldSetFlags()[0] = true;
}
if (isValidValue(fields()[1], other.ach)) {
this.ach = data().deepCopy(fields()[1].schema(), other.ach);
fieldSetFlags()[1] = true;
}
if (isValidValue(fields()[2], other.vcp)) {
this.vcp = data().deepCopy(fields()[2].schema(), other.vcp);
fieldSetFlags()[2] = true;
}
}
/**
* Gets the value of the 'tin' field.
* #return The value.
*/
public CharSequence getTin() {
return tin;
}
/**
* Sets the value of the 'tin' field.
* #param value The value of 'tin'.
* #return This builder.
*/
public pengine.Builder setTin(CharSequence value) {
validate(fields()[0], value);
this.tin = value;
fieldSetFlags()[0] = true;
return this;
}
/**
* Checks whether the 'tin' field has been set.
* #return True if the 'tin' field has been set, false otherwise.
*/
public boolean hasTin() {
return fieldSetFlags()[0];
}
/**
* Clears the value of the 'tin' field.
* #return This builder.
*/
public pengine.Builder clearTin() {
tin = null;
fieldSetFlags()[0] = false;
return this;
}
/**
* Gets the value of the 'ach' field.
* #return The value.
*/
public CharSequence getAch() {
return ach;
}
/**
* Sets the value of the 'ach' field.
* #param value The value of 'ach'.
* #return This builder.
*/
public pengine.Builder setAch(CharSequence value) {
validate(fields()[1], value);
this.ach = value;
fieldSetFlags()[1] = true;
return this;
}
/**
* Checks whether the 'ach' field has been set.
* #return True if the 'ach' field has been set, false otherwise.
*/
public boolean hasAch() {
return fieldSetFlags()[1];
}
/**
* Clears the value of the 'ach' field.
* #return This builder.
*/
public pengine.Builder clearAch() {
ach = null;
fieldSetFlags()[1] = false;
return this;
}
/**
* Gets the value of the 'vcp' field.
* #return The value.
*/
public CharSequence getVcp() {
return vcp;
}
/**
* Sets the value of the 'vcp' field.
* #param value The value of 'vcp'.
* #return This builder.
*/
public pengine.Builder setVcp(CharSequence value) {
validate(fields()[2], value);
this.vcp = value;
fieldSetFlags()[2] = true;
return this;
}
/**
* Checks whether the 'vcp' field has been set.
* #return True if the 'vcp' field has been set, false otherwise.
*/
public boolean hasVcp() {
return fieldSetFlags()[2];
}
/**
* Clears the value of the 'vcp' field.
* #return This builder.
*/
public pengine.Builder clearVcp() {
vcp = null;
fieldSetFlags()[2] = false;
return this;
}
#Override
#SuppressWarnings("unchecked")
public pengine build() {
try {
pengine record = new pengine();
record.tin = fieldSetFlags()[0] ? this.tin : (CharSequence) defaultValue(fields()[0]);
record.ach = fieldSetFlags()[1] ? this.ach : (CharSequence) defaultValue(fields()[1]);
record.vcp = fieldSetFlags()[2] ? this.vcp : (CharSequence) defaultValue(fields()[2]);
return record;
} catch (org.apache.avro.AvroMissingFieldException e) {
throw e;
} catch (Exception e) {
throw new org.apache.avro.AvroRuntimeException(e);
}
}
}
#SuppressWarnings("unchecked")
private static final org.apache.avro.io.DatumWriter<pengine>
WRITER$ = (org.apache.avro.io.DatumWriter<pengine>)MODEL$.createDatumWriter(SCHEMA$);
#Override public void writeExternal(java.io.ObjectOutput out)
throws java.io.IOException {
WRITER$.write(this, SpecificData.getEncoder(out));
}
#SuppressWarnings("unchecked")
private static final org.apache.avro.io.DatumReader<pengine>
READER$ = (org.apache.avro.io.DatumReader<pengine>)MODEL$.createDatumReader(SCHEMA$);
#Override public void readExternal(java.io.ObjectInput in)
throws java.io.IOException {
READER$.read(this, SpecificData.getDecoder(in));
}
#Override protected boolean hasCustomCoders() { return true; }
#Override public void customEncode(org.apache.avro.io.Encoder out)
throws java.io.IOException
{
out.writeString(this.tin);
out.writeString(this.ach);
out.writeString(this.vcp);
}
#Override public void customDecode(org.apache.avro.io.ResolvingDecoder in)
throws java.io.IOException
{
org.apache.avro.Schema.Field[] fieldOrder = in.readFieldOrderIfDiff();
if (fieldOrder == null) {
this.tin = in.readString(this.tin instanceof Utf8 ? (Utf8)this.tin : null);
this.ach = in.readString(this.ach instanceof Utf8 ? (Utf8)this.ach : null);
this.vcp = in.readString(this.vcp instanceof Utf8 ? (Utf8)this.vcp : null);
} else {
for (int i = 0; i < 3; i++) {
switch (fieldOrder[i].pos()) {
case 0:
this.tin = in.readString(this.tin instanceof Utf8 ? (Utf8)this.tin : null);
break;
case 1:
this.ach = in.readString(this.ach instanceof Utf8 ? (Utf8)this.ach : null);
break;
case 2:
this.vcp = in.readString(this.vcp instanceof Utf8 ? (Utf8)this.vcp : null);
break;
default:
throw new java.io.IOException("Corrupt ResolvingDecoder.");
}
}
}
}
}
The record.value() is a PaymentEngine object, so you can use record.value().getAch() to get the value of arc. The vcp is same. So you can compare to them with the requiered value. You get the json on print because print is automatically call the object's toString() method.
Related
I am trying create an object of the given class but am not able to cover the Arc.container().instance() lines. Could you please check what I am missing here. The code is given below;
/**
* Track Tag handler
*/
#Slf4j
#ApplicationScoped
#Unremovable
#SuppressWarnings({
"checkstyle:BooleanExpressionComplexity",
"PMD.ExcessiveClassLength",
"PMD.ExcessiveImports",
"PMD.GodClass",
"PMD.TooManyMethods"
})
public class TrackTagHandler implements TrackTagEventHandler
{
/**
* Tag service to handle business logic
*/
private final TagBusinessService tagBusinessService;
/**
* Number pool for generating new tag id
*/
private final NumberPoolService numberPoolService;
/**
* Topology data service for retrieving device entities
*/
private final TopologyLookupService topologyLookupService;
/**
* Device management gram service for creating device state request gram
*/
private final DeviceManagementService deviceManagementService;
/**
* Track handler for returning the track-tag form
*/
#SuppressWarnings("checkstyle:VisibilityModifier")
TrackTagTrackProvider trackTagTrackProvider;
/**
* Switch handler for returning the track-tag form
*/
#SuppressWarnings("checkstyle:VisibilityModifier")
TrackTagSwitchProvider trackTagSwitchProvider;
/**
* Roadway Worker & Blue Signal handler for returning the track-tag form
*/
#SuppressWarnings("checkstyle:VisibilityModifier")
TrackTagRoadwayBlueSignalProvider trackTagRoadwayBlueSignalProvider;
/**
* On Station handler for returning the track-tag form
*/
#SuppressWarnings("checkstyle:VisibilityModifier")
TrackTagOSProvider trackTagOSProvider;
/**
* Signal handler for returning the track-tag form
*/
#SuppressWarnings("checkstyle:VisibilityModifier")
TrackTagSignalProvider trackTagSignalProvider;
/**
* Misc. Device handler for returning the track-tag form
*/
#SuppressWarnings("checkstyle:VisibilityModifier")
TrackTagMiscDeviceProvider trackTagMiscDeviceProvider;
/**
* TrackTagHandler constructor<br>
* Use constructor injection to get the required services
*
* #param trackTagTrackProvider Track handler for returning the track-tag form
* #param trackTagSwitchProvider Switch handler for returning the track-tag form
* #param trackTagRoadwayBlueSignalProvider Roadway Worker & Blue Signal handler for returning the track-tag form
* #param trackTagOSProvider On Station handler for returning the track-tag form
* #param trackTagSignalProvider Signal handler for returning the track-tag form
* #param trackTagMiscDeviceProvider Misc. Device handler for returning the track-tag form
*/
#Inject
public TrackTagHandler(
final TrackTagTrackProvider trackTagTrackProvider,
final TrackTagSwitchProvider trackTagSwitchProvider,
final TrackTagRoadwayBlueSignalProvider trackTagRoadwayBlueSignalProvider,
final TrackTagOSProvider trackTagOSProvider,
final TrackTagSignalProvider trackTagSignalProvider,
final TrackTagMiscDeviceProvider trackTagMiscDeviceProvider)
{
this.trackTagTrackProvider = trackTagTrackProvider;
this.trackTagSwitchProvider = trackTagSwitchProvider;
this.trackTagRoadwayBlueSignalProvider = trackTagRoadwayBlueSignalProvider;
this.trackTagOSProvider = trackTagOSProvider;
this.trackTagSignalProvider = trackTagSignalProvider;
this.trackTagMiscDeviceProvider = trackTagMiscDeviceProvider;
this.tagBusinessService = Arc.container().instance(TagBusinessService.class).get();
this.numberPoolService = Arc.container().instance(NumberPoolService.class).get();
this.topologyLookupService = Arc.container().instance(TopologyLookupService.class).get();
this.deviceManagementService = Arc.container().instance(DeviceManagementService.class).get();
}
/**
* Handle event requesting
*/
#Override
#SuppressWarnings({
"PMD.ExcessiveMethodLength", "PMD.CyclomaticComplexity"
})
public TagEventContext handleEvent(final TagEventContext event)
{
event.setRequestInfo(event.getEvent().getBinding());
final RequestInfo requestInfo = event.getRequestInfo();
if (Optional.ofNullable(requestInfo).isEmpty())
{
throw new BadRequestException(MessageConstants.INVALID_REQUEST);
}
log.info("Calling track tag event handler for {}", event.getEvent());
switch (event.getEvent())
{
case Read:
final TagInfo tagInfo = getActiveTag(requestInfo.getTagId());
event.setTrackTag(setToTrackTag(tagInfo, event));
break;
case Pending:
// Set initials
final String dispatcherInitials =
event.getProperties().get(InitializeProperty.rtcInitials.getBinding()).toString();
final PendingRequestInfo pendingRequestInfo = PendingRequestInfo.builder()
.deviceIds(requestInfo.getDeviceIds())
.logicalPosition(requestInfo.getLogicalPosition())
.userId(requestInfo.getUserId())
.deviceType(requestInfo.getDeviceType())
.tagType(requestInfo.getTagType())
.build();
event.setTrackTag(createPendingTag(pendingRequestInfo, dispatcherInitials, event));
break;
case Update:
final UpdateRequestInfo updateRequestInfo = UpdateRequestInfo.builder()
.comments(requestInfo.getComments())
.estimatedRemovalTime(requestInfo.getEstimatedRemovalTime())
.logicalPosition(requestInfo.getLogicalPosition())
.name(requestInfo.getName())
.pseudo(requestInfo.isPseudo())
.tagId(requestInfo.getTagId())
.timeZone(requestInfo.getTimeZone())
.userId(requestInfo.getUserId())
.build();
final TagData tagData = getTagData(requestInfo.getTagId());
if (tagData.getCurrentTagInfo().getStatus() == TagStatus.PENDING)
{
createActiveTag(updateRequestInfo, event);
}
else
{
updateTag(updateRequestInfo, event);
}
break;
case Remove:
final DeleteRequestInfo deleteRequestInfo = DeleteRequestInfo.builder()
.comments(requestInfo.getComments())
.logicalPosition(requestInfo.getLogicalPosition())
.tagId(requestInfo.getTagId())
.userId(requestInfo.getUserId())
.build();
deleteTag(deleteRequestInfo, event);
break;
case RemovalComments:
final UpdateDeleteRequestInfo updateDeleteRequestInfo = UpdateDeleteRequestInfo.builder()
.action(requestInfo.getAction())
.removalComments(requestInfo.getRemovalComments())
.tagId(requestInfo.getTagId())
.build();
updateRemovalCommentsToDeleteTag(updateDeleteRequestInfo, event);
break;
case Reset:
final TagInfo resetTagInfo = resetTag(requestInfo.getTagId(), event);
event.setTrackTag(setToTrackTag(resetTagInfo, event));
break;
case Cancel:
cancelTag(requestInfo.getTagId(), event);
break;
case DeviceRecord:
final DeviceUpdateRequestInfo deviceUpdateRequestInfo = DeviceUpdateRequestInfo.builder()
.deviceAction(requestInfo.getDeviceAction())
.logicalPosition(requestInfo.getLogicalPosition())
.recordId(requestInfo.getRecordId())
.userId(requestInfo.getUserId())
.build();
updateDeviceRecord(deviceUpdateRequestInfo);
break;
case TagTypeChange:
final TagTypeUpdateRequestInfo tagTypeUpdateRequestInfo = TagTypeUpdateRequestInfo.builder()
.logicalPosition(requestInfo.getLogicalPosition())
.tagId(requestInfo.getTagId())
.deviceType(requestInfo.getDeviceType())
.tagType(requestInfo.getTagType())
.userId(requestInfo.getUserId())
.build();
final TagInfo modifiedTagInfo = updateTagType(tagTypeUpdateRequestInfo);
event.setTrackTag(setToTrackTag(modifiedTagInfo, event));
break;
default:
}
return event;
}
}
TrackTagEvent
/**
* Track Tag form events with handlers
*/
public enum TrackTagEvent implements DynamicFormEvent<TagEventContext>
{
/** **/
Read
{
#Override
public TagEventContext handleEvent(final TagEventContext event)
{
return Arc.container().instance(TrackTagHandler.class).get().handleEvent(event);
}
},
/** **/
Pending
{
#Override
public TagEventContext handleEvent(final TagEventContext event)
{
return Arc.container().instance(TrackTagHandler.class).get().handleEvent(event);
}
},
/** **/
Update
{
#Override
public TagEventContext handleEvent(final TagEventContext event)
{
return Arc.container().instance(TrackTagHandler.class).get().handleEvent(event);
}
},
/** **/
Remove
{
#Override
public TagEventContext handleEvent(final TagEventContext event)
{
return Arc.container().instance(TrackTagHandler.class).get().handleEvent(event);
}
},
/** **/
RemovalComments
{
#Override
public TagEventContext handleEvent(final TagEventContext event)
{
return Arc.container().instance(TrackTagHandler.class).get().handleEvent(event);
}
},
/** **/
Reset
{
#Override
public TagEventContext handleEvent(final TagEventContext event)
{
return Arc.container().instance(TrackTagHandler.class).get().handleEvent(event);
}
},
/** **/
Cancel
{
#Override
public TagEventContext handleEvent(final TagEventContext event)
{
return Arc.container().instance(TrackTagHandler.class).get().handleEvent(event);
}
},
/** **/
DeviceRecord
{
#Override
public TagEventContext handleEvent(final TagEventContext event)
{
return Arc.container().instance(TrackTagHandler.class).get().handleEvent(event);
}
},
/** **/
TagTypeChange
{
#Override
public TagEventContext handleEvent(final TagEventContext event)
{
return Arc.container().instance(TrackTagHandler.class).get().handleEvent(event);
}
};
/** The binding value */
#Getter
private final String binding;
/** Constructor to set binding to property name */
TrackTagEvent()
{
this.binding = name();
}
/** Constructor to set binding to given value */
TrackTagEvent(final String binding)
{
this.binding = binding;
}
}
TagEventContext
/**
* Track Tag Event
*/
#Slf4j
#Data
#NoArgsConstructor
public class TagEventContext implements TrackTagEventContext<TrackTag>
{
/**
* Serializable
*/
private static final long serialVersionUID = 1L;
/**
* Id
*/
private long id;
/**
* Form id
*/
private String formId;
/**
* Form version
*/
private String version;
/**
* Event
*/
private TrackTagEvent event;
/**
* Control list
*/
private List<ControlListItem> controlList;
/**
* Properties
*/
private Map<String, Object> properties;
/**
* Dynamic Form update
*/
private DynamicForm dynamicForm;
/**
* Feedback
*/
private Feedback feedback;
/**
* The Track Tag Information
*/
#JsonIgnore
private TrackTag trackTag;
/**
* Request Information
*/
#JsonIgnore
private RequestInfo requestInfo;
/**
* The publish type
*/
#JsonIgnore
private PublishType publishType = PublishType.NO_PUBLISH;
/**
* Set dynamic form and copy required fields to context
*/
#Override
public void setDynamicForm(final DynamicForm form)
{
this.dynamicForm = form;
if (form != null && form.getMetadata() != null)
{
this.formId = form.getMetadata().getFormId();
this.version = Integer.toString(form.getVersion());
}
}
/**
* Update control list
*/
#Override
public List<ControlListItem> updateControlListFromTrackTag()
{
if (trackTag == null)
{
log.debug("No tag specified");
return controlList;
}
id = trackTag.getTagId();
updateControlListWithDefaultValues();
return controlList;
}
/**
* Get RequestInfo object from properties based on request
*/
private RequestInfo fetchRequestInfo(final String request)
{
final ObjectMapper mapper = new ObjectMapper();
try
{
mapper.registerModule(new JavaTimeModule());
final String jsonValue = mapper.writeValueAsString(( properties.get(request) ));
return mapper.readValue(jsonValue, RequestInfo.class);
}
catch (final JsonProcessingException ex)
{
log.error(ex.getMessage());
return null;
}
}
/**
* Set RequestInfo object to context
*/
public void setRequestInfo(final String request)
{
final RequestInfo info = fetchRequestInfo(request);
this.requestInfo = info;
}
}
After covering the costructor, I am supposed to cover the handleEvent() method. But am stuck at the initial place only.
I tried writing few testcases, but unable to understand how to cover that Arc.container().instance(). Below is what I have tried so far;
class TrackTagHandlerTest
{
final ObjectMapper mapper = new ObjectMapper().disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
#Inject TrackTagHandler trackTagHandler;
#Inject TrackTagProvider trackTagProvider;
#Inject TrackTagSwitchProvider trackTagSwitchProvider;
#Inject TrackTagRoadwayBlueSignalProvider trackTagRoadwayBlueSignalProvider;
#Inject TrackTagOSProvider trackTagOSProvider;
#Inject TrackTagSignalProvider trackTagSignalProvider;
#Inject TrackTagMiscDeviceProvider trackTagMiscDeviceProvider;
#Inject TagBusinessService tagBusinessService;
#Test
void testTrackTagHandleEvent() throws IOException
{
InputData request = mapper
.readValue(Thread.currentThread().getContextClassLoader().getResource("request.json"), InputData.class);
TrackTagTrackProvider trackTagProvider = new TrackTagTrackProvider();
TagEventContext context = trackTagProvider.createEvent(TrackTagEvent.Read, request);
context.setDynamicForm(new DynamicForm());
context.setFeedback(new Feedback());
assertNotNull(context.getProperties());
TrackTagHandler th = new TrackTagHandler(trackTagProvider, trackTagSwitchProvider, trackTagRoadwayBlueSignalProvider, trackTagOSProvider, trackTagSignalProvider, trackTagMiscDeviceProvider);
TagEventContext returnContext = TrackTagEvent.Read.handleEvent(context);
}
}
I have a Request Class that contains another class object. Now I want to parse a nested JSON as Request Class Object.
I am trying using ObjectMapper but its throwing exceptions. Please help me to do that using java Jackson.
{
"filters":[
{
"key":"CustomerId",
"op":"=",
"value" : "1"
},
{
"key":"userName",
"op":"=",
"value" : "admin"
}
],
"startIndex" : 1,
"size" : 10,
"OrderBy" :
{
"propertyId" : "userName",
"Desc" : false
},
"TimeRange" : {
"startTimestamp" : 1,
"endTimestamp" :10
}
}
Logic:
public static class OrderBy {
private String propertyId;
private boolean desc = true;
}
class TimeRange {
private Long startTimestamp;
private Long endTimestamp;
}
class Filter {
private String propertyId;
private String op;
private Object value;
}
public class Request {
private List<Filter> filters;
private TimeRange timeRange;
private OrderBy orderBy;
private int startIndex = 0;
private int size = 20;
}
I give you a Jaskson Util Tools class.
package com.jackson.utils;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* The class JacksonUtil
*
* json字符与对像转换
*
* #version: $Revision$ $Date$ $LastChangedBy$
*
*/
public final class JacksonUtil {
public static ObjectMapper objectMapper;
/**
* 使用泛型方法,把json字符串转换为相应的JavaBean对象。
* (1)转换为普通JavaBean:readValue(json,Student.class)
* (2)转换为List,如List<Student>,将第二个参数传递为Student
* [].class.然后使用Arrays.asList();方法把得到的数组转换为特定类型的List
*
* #param jsonStr
* #param valueType
* #return
*/
public static <T> T readValue(String jsonStr, Class<T> valueType) {
if (objectMapper == null) {
objectMapper = new ObjectMapper();
}
try {
return objectMapper.readValue(jsonStr, valueType);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* json数组转List
* #param jsonStr
* #param valueTypeRef
* #return
*/
public static <T> T readValue(String jsonStr, TypeReference<T> valueTypeRef){
if (objectMapper == null) {
objectMapper = new ObjectMapper();
}
try {
return objectMapper.readValue(jsonStr, valueTypeRef);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 把JavaBean转换为json字符串
*
* #param object
* #return
*/
public static String toJSon(Object object) {
if (objectMapper == null) {
objectMapper = new ObjectMapper();
}
try {
return objectMapper.writeValueAsString(object);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
Now ,you can use blow code covert to Request :
String userBeanToJson = "your json string";//please replace this for your need json
Request jsonToUserBean = JacksonUtil.readValue(userBeanToJson, Request.class);
Hope it is useful for you.
I have a recursive method that steps through a large directory containing thousands of music files. It adds a music file to an observableList<> each time the extension meets the criteria. The list is hooked into a TableView<> in a different thread prior to the recursive method executing so that the user can see the files being added to the TableView<> in real time.
The problem is I know very little about how to manage memory in java and think I might be getting in the way of garbage collection. The recursive method eats up almost 6 GB of ram after around 3,000 songs and then begins to ignore files that it should be able to read. Furthermore, after it has 'finished' stepping through the directory structure, the ram does not reduce, (i.e. the stack from the recursive method is not being destroyed and I think all the objects that are referenced are still in heap memory).
It goes further.. I export the playlist to an XML file and close the program. When I relaunch it, the memory is completely reasonable, so i know its not the large list containing the files, it must have something to do with the recursive method.
Here's the recusive method located in the music handler:
/**
* method used to seek all mp3 files in a specified directory and save them
* to an ObservableArrayList
*
* #param existingSongs
* #param directory
* #return
* #throws FileNotFoundException
* #throws UnsupportedEncodingException
*/
protected ObservableList<FileBean> digSongs(ObservableList<FileBean> existingSongs,
File directory) throws FileNotFoundException,
UnsupportedEncodingException {
/*
* Each directory is broken into a list and passed back into the digSongs().
*/
if (directory.isDirectory() && directory.canRead()) {
File[] files = directory.listFiles();
for (int i = 0; i < files.length; i++) {
digSongs(existingSongs, files[i]);
}
/*
* if a file is not a directory, then is it checked to see if it's
* an mp3 file
*/
} else if (directory.getAbsolutePath().endsWith(".mp3")
|| directory.getAbsolutePath().endsWith(".m4a")
) {
FileBean songBean = new FileBean(directory).getSerializableJavaBean();
existingSongs.add(songBean);
songBean.getPlayer().setOnReady(new OnMediaReadyEvent(songBean));
songBean.getPlayer().setOnError(new OnMediaPlayerStalled(existingSongs, songBean));
/*
* if it's not a directory or mp3 file, then do nothing
*/
} else {
return existingSongs;
}
return existingSongs;
}
Here is the listener for the MediaPlayer used to read thr ID tags if possible, this is also located in the music handler
/**
* This class will populate the FileBean metaData after the MediaPlayer's
* status has been changed to READY. Uses the FileBean's setter methods so
* that they will be picked up by the XMLEncoder. This allows the use of the
* Media's ID3v2 tag reading abilities. If tags are not read due to
* incompatibility, they are not changed.
*
* This step is computationally expensive but should not need to be done
* very often and it saves a ton of memory during normal use. Setting the
* Media and MediaPlayer objects to null make this run much faster and uses
* less memory
*
* #author Karottop
*
*/
protected class OnMediaReadyEvent implements Runnable {
private FileBean fileBean;
public OnMediaReadyEvent(FileBean fileBean) {
this.fileBean = fileBean;
}
#Override
public void run() {
String songName = null;
String album = null;
String artist = null;
double duration = 0.0;
try{
// Retrieve track song title
songName = (String) fileBean.getMedia().getMetadata()
.get("title");
// Retrieve Album title
album = (String) fileBean.getMedia().getMetadata()
.get("album");
// Retrieve Artist title
artist = (String) fileBean.getMedia().getMetadata()
.get("artist");
// Retrieve Track duration
duration = fileBean.getMedia().getDuration().toMinutes();
}catch(NullPointerException e){
System.out.println(e.getMessage());
}
// Set track song title
if (songName != null)
fileBean.setSongName(songName);
// Set Album title
if (album != null)
fileBean.setAlbum(album);
// Retrieve and set Artist title
if (artist != null)
fileBean.setArtist(artist);
// Set Track duration
fileBean.setDuration(Double.parseDouble(
XMLMediaPlayerHelper.convertDecimalMinutesToTimeMinutes(duration)));
fileBean.setMedia(null);
fileBean.setPlayer(null);
}
}
Here is where I call the method in the controller for the FXML:
public class LoadAllMusicFiles implements Runnable{
private TableView<FileBean> tableView;
public LoadAllMusicFiles(TableView<FileBean> tableView) {
this.tableView = tableView;
}
#Override
public void run() {
try {
musicHandler.loadAllPlaylists();
tableView.setItems(musicHandler.getMainPlaylist().getSongsInPlaylist());
playlistTable.setItems(musicHandler.getPlaylists());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (NoPlaylistsFoundException e) {
String title = "Mine for mp3s";
String header = "No playlists were found.\n"
+ "These are your mp3 mining options...";
String content = "Do you want to import a single mp3\n"
+ "or a folder containing many mp3s?\n\n"
+ "**Note For large volumes of songs this may take a while.\n"
+ "Grab some coffee or something..**";
findNewSongs(title, header, content);
// need to handle file not found exception in new thread
tableView.setItems(musicHandler.getMainPlaylist().getSongsInPlaylist());
playlistTable.setItems(musicHandler.getPlaylists());
Platform.runLater(new SelectIndexOnTable(playlistTable, 0));
tableView.getSelectionModel().selectFirst();
}
}
}
/**
* The method will display an Alert box prompting the user to locate a
* song or directory that contains mp3s
*
* The parameters passed is the text the user will see in the Alert box.
* The Alert box will come with 3 new buttons: 1)Single mp3, 2)Folder of mp3s
* and 3)Cancel. If the user selects the first button they will be
* presented with a FileChooser display to select a song. If they press
* the second button, the user will be prompted with a DirectoryChooser
* display. The third button displays nothing and closes the Alert box.
*
* The following outlines where each parameter will be displayed in the
* Alert box
*
* title: very top of the box in the same latitude as the close button.
* header: inside the Alert box at the top.
* content: in the middle of the box. This is the best place to explain
* the button options to the user.
* #param title
* #param header
* #param content
*/
private void findNewSongs(String title, String header, String content){
Alert importType = new Alert(AlertType.CONFIRMATION);
importType.setTitle(title);
importType.setHeaderText(header);
importType.setContentText(content);
ButtonType singleMp3 = new ButtonType("Single mp3");
ButtonType folderOfmp3s = new ButtonType("Folder Of mp3s");
ButtonType cancel = new ButtonType("Cancel", ButtonData.CANCEL_CLOSE);
importType.getButtonTypes().setAll(singleMp3, folderOfmp3s, cancel);
Optional<ButtonType> result = importType.showAndWait();
if(result.get() == singleMp3){
FileChooser fileChooser = new FileChooser();
fileChooser.setTitle("Location of mp3s");
ArrayList<String> extensions = new ArrayList<>();
extensions.add("*.mp3");
fileChooser.getExtensionFilters().add(
new ExtensionFilter("Audio Files", getSupportedFileTypes()));
File selectedFile = fileChooser.showOpenDialog(playBackButton.getScene().getWindow());
if(selectedFile == null){
return;
}
Thread findSongs = new Thread(new DigSongs(selectedFile.getAbsolutePath()));
findSongs.start();
}else if(result.get() == folderOfmp3s){
DirectoryChooser fileChooser = new DirectoryChooser();
fileChooser.setTitle("Location to mine for mp3s");
File selectedFile = fileChooser.showDialog(playBackButton.getScene().getWindow());
if(selectedFile == null){
return;
}
Thread findSongs = new Thread(new DigSongs(selectedFile.getAbsolutePath()));
findSongs.start();
}else{
return;
}
}
public class DigSongs implements Runnable{
String path;
public DigSongs(String path) {
this.path = path;
}
#Override
public void run() {
Platform.runLater(new UpdateLabel(digLabel, "loading..."));
try {
musicHandler.findNewSongs(path);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
ObservableList<FileBean> songArray = musicHandler.getMainPlaylist().getSongsInPlaylist();
Platform.runLater(new UpdateLabel(digLabel, "complete: " + songArray.size()));
}
}
This method is located in the music handler and basically just calls the recursive method digSongs(ObservableList, File):
/**
* This method will search for songs in a new directory and add them to the song list
* in the main playlist
* #param newDirectory
* #return
* #throws FileNotFoundException
* #throws UnsupportedEncodingException
*/
public PlaylistBean findNewSongs(String newDirectory)
throws FileNotFoundException, UnsupportedEncodingException{
PlaylistBean main = getMainPlaylist();
File file = new File(newDirectory);
// add new songs to existing main playlist
digSongs(main.getSongsInPlaylist(), file);
return main;
}
Guys, I know this is a lot of code and stuff to read. I just can't seem to find the answers I need on google. I suspect the problem has something to do with the reference being passed to the TableView<> but I honestly don't know. I hope someone can take the time to look. I'll post more code if anyone needs it
EDIT: FileBean class
package fun.personalUse.dataModel;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Comparator;
import javafx.beans.property.SimpleStringProperty;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
/**
* Data model for use with a media player. This object is intended to store
* song data for 1 song
* #author Karottop
*
*/
public class FileBean implements Comparator<FileBean>, Comparable<FileBean>{
private File file;
private SimpleStringProperty location;
private SimpleStringProperty songName;
private SimpleStringProperty album;
private SimpleStringProperty artist;
private SimpleStringProperty url;
private Media media;
private MediaPlayer player;
private SimpleStringProperty duration;
/**
* inserts default or null values for every field. This constructor
* should be used when making a serializable FileBean. setters should
* be used to initialize the object
*/
public FileBean(){
media = null;
file = null;
location = new SimpleStringProperty();
songName = new SimpleStringProperty();
album = new SimpleStringProperty();
artist = new SimpleStringProperty();
url = new SimpleStringProperty();
/**
* must initialize with a number because this field will be called
* before the MediaPlayer's status has changed which would cause a
* null pointer exception to be thrown if not initialized
*/
duration = new SimpleStringProperty("0.0");
}
/**
* Initializes the file bean using a file
* #param file
* #throws FileNotFoundException
* #throws UnsupportedEncodingException
*/
public FileBean(File file) throws FileNotFoundException, UnsupportedEncodingException{
location = new SimpleStringProperty();
songName = new SimpleStringProperty();
album = new SimpleStringProperty();
artist = new SimpleStringProperty();
url = new SimpleStringProperty();
/**
* must initialize with a number because this field will be called
* before the MediaPlayer's status has changed which would cause a
* null pointer exception to be thrown if not initialized
*/
duration = new SimpleStringProperty("0.0");
this.file = file;
location.set(file.getAbsolutePath().replace("\\", "/"));
/*
* encode all special characters.
* URLEncoder puts a '+' where a ' ' is so change all '+' to encoded space '%20'.
*/
url.set(URLEncoder.encode(location.get(), "UTF-8").replace("+", "%20"));
/*
* Could not easily figure out how to set an action event for when the Media
* object is done loading. Using the MediaPlayer status change event instead.
* Looking for a better option
*/
media = new Media("file:///" + url.get());
this.player = new MediaPlayer(media);
setDefaultSongNameAndArtist();
}
public FileBean(String absolutePath) throws FileNotFoundException, UnsupportedEncodingException{
this(new File(absolutePath));
}
/**
* This method uses the parent directory strucutre to guesstimate
* what the song name, artist and album name is. a '?' is appended at the
* end of each item to indicate this is a guessed value
*
* media file that do not adhere to the following directory structure
* will not be named correctly:
*
* pathToMedia/Artist/Album/song
*/
private void setDefaultSongNameAndArtist(){
String[] songLocation = getLocation().split("/");
String[] songFragment = songLocation[songLocation.length - 1].split("[.]");
setSongName(songFragment[0]);
setAlbum(songLocation[songLocation.length - 2] + "?");
setArtist(songLocation[songLocation.length - 3] + "?");
}
/**
* #return the player
*/
public MediaPlayer getPlayer() {
return player;
}
/**
* #param player the player to set
*/
public void setPlayer(MediaPlayer player) {
this.player = player;
}
/**
* #return the duration
*/
public double getDuration() {
return Double.parseDouble(duration.get());
}
/**
* #param duration the duration to set
*/
public void setDuration(double duration) {
this.duration.set(String.format("%.2f", duration));
}
/**
* #return the album
*/
public String getAlbum() {
return album.get();
}
/**
* #param album the album to set
*/
public void setAlbum(String album) {
this.album.set(album);
}
/**
* #return the artist
*/
public String getArtist() {
return artist.get();
}
/**
* #param artist the artist to set
*/
public void setArtist(String artist) {
this.artist.set(artist);
}
/**
* #return the media
*/
public Media getMedia() {
return media;
}
/**
* #param media the media to set
*/
public void setMedia(Media media) {
this.media = media;
}
/**
* #return the url
*/
public String getUrl() {
return url.get();
}
/**
* #param url the url to set
*/
public void setUrl(String url) {
this.url.set(url);
}
/**
* #return the file
*/
public File getFile() {
return file;
}
/**
* #param file the file to set
*/
public void setFile(File file) {
this.file = file;
}
/**
* #return the location
*/
public String getLocation() {
return location.get();
}
/**
* #param location the location to set
*/
public void setLocation(String location) {
this.location.set(location);
}
/**
* #return the name
*/
public String getSongName() {
return songName.get();
}
/**
* #param name the name to set
*/
public void setSongName(String name) {
this.songName.set(name);
}
/**
* returns the songName property
* #return
*/
public SimpleStringProperty songNameProperty(){
return songName;
}
/**
* returns the artist property
* #return
*/
public SimpleStringProperty artistProperty(){
return artist;
}
/**
* returns the album property
* #return
*/
public SimpleStringProperty albumProperty(){
return album;
}
/**
* returns the duration property
* #return
*/
public SimpleStringProperty durationProperty(){
return duration;
}
/**
* Creates a serializable copy of this object
* by using it's setters. The purpose of this
* method is so that the FileBean objects can
* be exported to an XML
* #return
*/
public FileBean getSerializableJavaBean(){
FileBean temp = new FileBean();
temp.setAlbum(this.getAlbum());
temp.setArtist(this.getArtist());
temp.setDuration(this.getDuration());
temp.setFile(this.getFile());
temp.setLocation(this.getLocation());
temp.setMedia(this.getMedia());
temp.setPlayer(player);
temp.setSongName(this.getSongName());
temp.setUrl(this.getUrl());
return temp;
}
/**
* Method used to return a fully populated FileBean after decoded from XML.
* #return
*/
public FileBean getFullFileBean(){
try {
return new FileBean(new File(getLocation()));
} catch (FileNotFoundException | UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
FileBean temp = new FileBean();
temp.setLocation("error");
return temp;
}
}
/**
* Returns are string in the following format:
*
* [song name], [artist name], [album name]
*/
#Override
public String toString(){
return String.format("%s, %s, %s", getSongName(), getArtist(), getAlbum());
}
/**
* uses FileBean.toSting().compareTo(this.toString()) to determine if the two
* beans are equal
*/
#Override
public boolean equals(Object fileBean){
FileBean newBean = (FileBean)fileBean;
return newBean.toString().compareTo(this.toString()) == 0;
}
/**
* Uses the String.compare() to order FileBeans based on their absolute path
*/
#Override
public int compareTo(FileBean bean) {
if(this.getLocation().compareTo(bean.getLocation()) > 0){
return 1;
}else if(this.getLocation().compareTo(bean.getLocation()) < 0){
return -1;
} else{
return 0;
}
}
/**
* uses the compareTo method to compare two files beans.
*
* This method uses the String.compare() to order FileBeans
* based on their absolute path
*/
#Override
public int compare(FileBean bean1, FileBean bean2) {
// TODO Auto-generated method stub
return bean1.compareTo(bean2);
}
}
It is almost always a bad idea to use File.listFiles() because it eagerly allocates an array of files which may be very memory consuming.
So recursive digSongs method may produce significant peak memory usage (or even lead to OutOfMemoryError).
Take a look at Files.walkFileTree(...). It is a great memory efficient solution for directory traversal.
For an application I'm developing, a lot of older clients with larger fingers and poor eyesight need the selection arrows and Date/Year labels to be larger on the DatePicker Pop-Up.
In the documentation of the DatePicker class, there are methods described for modifying the DateCells within the calendar itself, but nothing describes or elaborates on the construction of the popup any further. (At least, the code is not self-documented plainly enough for my entry-level knowledge of Java development to grasp any hints)
My implementation ends up looking like this
Sometimes when I open and close the calendar, the size of the font and buttons in the Month/Year controls fluctuate, but I have no idea how or why. I assume some CSS may be tampering with it, but I have no idea why it would sometimes work and sometimes not. CSS' true nature has always escaped me.
I know it [DatePicker] is based on a ComboBox, but I have absolutely zero idea how it is constructed and fully implemented, much less how to modify very specific elements in it.
Is there a method I can call/Override, or a parameter I can modify in-line to style these properly? Also, could there be a combination of classes and elements I can call in CSS to style these to avoid having to outright modify DatePicker itself as described above?
Here is my implementation:
#FXML private DatePicker dPicker;
final Callback<DatePicker, DateCell> dayCellFactory =
new Callback<DatePicker, DateCell>() {
#Override
public DateCell call(final DatePicker datePicker) {
return new DateCell() {
#Override
public void updateItem(LocalDate item, boolean empty) {
super.updateItem(item, empty);
setMinSize(100,100);
setStyle("-fx-font-size:50px;-fx-font-weight:bold;");
}
};
}
};
dPicker.setDayCellFactory(dayCellFactory);
...and here is DataPicker.java:
/*
* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package javafx.scene.control;
// editor and converter code in sync with ComboBox 4858:e60e9a5396e6
import java.time.LocalDate;
import java.time.DateTimeException;
import java.time.chrono.Chronology;
import java.time.chrono.ChronoLocalDate;
import java.time.chrono.IsoChronology;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.format.DecimalStyle;
import java.time.format.FormatStyle;
import java.time.temporal.TemporalAccessor;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import com.sun.javafx.scene.control.skin.ComboBoxListViewSkin;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.beans.property.SimpleObjectProperty;
import javafx.css.CssMetaData;
import javafx.css.Styleable;
import javafx.css.StyleableBooleanProperty;
import javafx.css.StyleableProperty;
import javafx.util.Callback;
import javafx.util.StringConverter;
import com.sun.javafx.css.converters.BooleanConverter;
import com.sun.javafx.scene.control.skin.DatePickerSkin;
import com.sun.javafx.scene.control.skin.resources.ControlResources;
/**
* The DatePicker control allows the user to enter a date as text or
* to select a date from a calendar popup. The calendar is based on
* either the standard ISO-8601 chronology or any of the other
* chronology classes defined in the java.time.chrono package.
*
* <p>The {#link #valueProperty() value} property represents the
* currently selected {#link java.time.LocalDate}. An initial date can
* be set via the {#link #DatePicker(java.time.LocalDate) constructor}
* or by calling {#link #setValue(java.time.LocalDate) setValue()}. The
* default value is null.
*
* <pre><code>
* final DatePicker datePicker = new DatePicker();
* datePicker.setOnAction(new EventHandler() {
* public void handle(Event t) {
* LocalDate date = datePicker.getValue();
* System.err.println("Selected date: " + date);
* }
* });
* </code></pre>
*
* The {#link #chronologyProperty() chronology} property specifies a
* calendar system to be used for parsing, displaying, and choosing
* dates.
* The {#link #valueProperty() value} property is always defined in
* the ISO calendar system, however, so applications based on a
* different chronology may use the conversion methods provided in the
* {#link java.time.chrono.Chronology} API to get or set the
* corresponding {#link java.time.chrono.ChronoLocalDate} value. For
* example:
*
* <pre><code>
* LocalDate isoDate = datePicker.getValue();
* ChronoLocalDate chronoDate =
* ((isoDate != null) ? datePicker.getChronology().date(isoDate) : null);
* System.err.println("Selected date: " + chronoDate);
* </code></pre>
*
*
* #since JavaFX 8.0
*/
public class DatePicker extends ComboBoxBase<LocalDate> {
private LocalDate lastValidDate = null;
private Chronology lastValidChronology = IsoChronology.INSTANCE;
/**
* Creates a default DatePicker instance with a <code>null</code> date value set.
*/
public DatePicker() {
this(null);
valueProperty().addListener(new InvalidationListener() {
#Override public void invalidated(Observable observable) {
LocalDate date = getValue();
Chronology chrono = getChronology();
if (validateDate(chrono, date)) {
lastValidDate = date;
} else {
System.err.println("Restoring value to " +
((lastValidDate == null) ? "null" : getConverter().toString(lastValidDate)));
setValue(lastValidDate);
}
}
});
chronologyProperty().addListener(new InvalidationListener() {
#Override public void invalidated(Observable observable) {
LocalDate date = getValue();
Chronology chrono = getChronology();
if (validateDate(chrono, date)) {
lastValidChronology = chrono;
} else {
System.err.println("Restoring value to " + lastValidChronology);
setChronology(lastValidChronology);
}
}
});
}
private boolean validateDate(Chronology chrono, LocalDate date) {
try {
if (date != null) {
chrono.date(date);
}
return true;
} catch (DateTimeException ex) {
System.err.println(ex);
return false;
}
}
/**
* Creates a DatePicker instance and sets the
* {#link #valueProperty() value} to the given date.
*
* #param localDate to be set as the currently selected date in the DatePicker. Can be null.
*/
public DatePicker(LocalDate localDate) {
setValue(localDate);
getStyleClass().add(DEFAULT_STYLE_CLASS);
setEditable(true);
}
/***************************************************************************
* *
* Properties *
* *
**************************************************************************/
/**
* A custom cell factory can be provided to customize individual
* day cells in the DatePicker popup. Refer to {#link DateCell}
* and {#link Cell} for more information on cell factories.
* Example:
*
* <pre><code>
* final Callback<DatePicker, DateCell> dayCellFactory = new Callback<DatePicker, DateCell>() {
* public DateCell call(final DatePicker datePicker) {
* return new DateCell() {
* #Override public void updateItem(LocalDate item, boolean empty) {
* super.updateItem(item, empty);
*
* if (MonthDay.from(item).equals(MonthDay.of(9, 25))) {
* setTooltip(new Tooltip("Happy Birthday!"));
* setStyle("-fx-background-color: #ff4444;");
* }
* if (item.equals(LocalDate.now().plusDays(1))) {
* // Tomorrow is too soon.
* setDisable(true);
* }
* }
* };
* }
* };
* datePicker.setDayCellFactory(dayCellFactory);
* </code></pre>
*
* #defaultValue null
*/
private ObjectProperty<Callback<DatePicker, DateCell>> dayCellFactory;
public final void setDayCellFactory(Callback<DatePicker, DateCell> value) {
dayCellFactoryProperty().set(value);
}
public final Callback<DatePicker, DateCell> getDayCellFactory() {
return (dayCellFactory != null) ? dayCellFactory.get() : null;
}
public final ObjectProperty<Callback<DatePicker, DateCell>> dayCellFactoryProperty() {
if (dayCellFactory == null) {
dayCellFactory = new SimpleObjectProperty<Callback<DatePicker, DateCell>>(this, "dayCellFactory");
}
return dayCellFactory;
}
/**
* The calendar system used for parsing, displaying, and choosing
* dates in the DatePicker control.
*
* <p>The default value is returned from a call to
* {#code Chronology.ofLocale(Locale.getDefault(Locale.Category.FORMAT))}.
* The default is usually {#link java.time.chrono.IsoChronology} unless
* provided explicitly in the {#link java.util.Locale} by use of a
* Locale calendar extension.
*
* Setting the value to <code>null</code> will restore the default
* chronology.
*/
public final ObjectProperty<Chronology> chronologyProperty() {
return chronology;
}
private ObjectProperty<Chronology> chronology =
new SimpleObjectProperty<Chronology>(this, "chronology", null);
public final Chronology getChronology() {
Chronology chrono = chronology.get();
if (chrono == null) {
try {
chrono = Chronology.ofLocale(Locale.getDefault(Locale.Category.FORMAT));
} catch (Exception ex) {
System.err.println(ex);
}
if (chrono == null) {
chrono = IsoChronology.INSTANCE;
}
//System.err.println(chrono);
}
return chrono;
}
public final void setChronology(Chronology value) {
chronology.setValue(value);
}
/**
* Whether the DatePicker popup should display a column showing
* week numbers.
*
* <p>The default value is specified in a resource bundle, and
* depends on the country of the current locale.
*/
public final BooleanProperty showWeekNumbersProperty() {
if (showWeekNumbers == null) {
String country = Locale.getDefault(Locale.Category.FORMAT).getCountry();
boolean localizedDefault =
(!country.isEmpty() &&
ControlResources.getNonTranslatableString("DatePicker.showWeekNumbers").contains(country));
showWeekNumbers = new StyleableBooleanProperty(localizedDefault) {
#Override public CssMetaData<DatePicker,Boolean> getCssMetaData() {
return StyleableProperties.SHOW_WEEK_NUMBERS;
}
#Override public Object getBean() {
return DatePicker.this;
}
#Override public String getName() {
return "showWeekNumbers";
}
};
}
return showWeekNumbers;
}
private BooleanProperty showWeekNumbers;
public final void setShowWeekNumbers(boolean value) {
showWeekNumbersProperty().setValue(value);
}
public final boolean isShowWeekNumbers() {
return showWeekNumbersProperty().getValue();
}
// --- string converter
/**
* Converts the input text to an object of type LocalDate and vice
* versa.
*
* <p>If not set by the application, the DatePicker skin class will
* set a converter based on a {#link java.time.DateTimeFormatter}
* for the current {#link java.util.Locale} and
* {#link #chronologyProperty() chronology}. This formatter is
* then used to parse and display the current date value.
*
* Setting the value to <code>null</code> will restore the default
* converter.
*
* <p>Example using an explicit formatter:
* <pre><code>
* datePicker.setConverter(new StringConverter<LocalDate>() {
* String pattern = "yyyy-MM-dd";
* DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern(pattern);
*
* {
* datePicker.setPromptText(pattern.toLowerCase());
* }
*
* #Override public String toString(LocalDate date) {
* if (date != null) {
* return dateFormatter.format(date);
* } else {
* return "";
* }
* }
*
* #Override public LocalDate fromString(String string) {
* if (string != null && !string.isEmpty()) {
* return LocalDate.parse(string, dateFormatter);
* } else {
* return null;
* }
* }
* });
* </code></pre>
* <p>Example that wraps the default formatter and catches parse exceptions:
* <pre><code>
* final StringConverter<LocalDate> defaultConverter = datePicker.getConverter();
* datePicker.setConverter(new StringConverter<LocalDate>() {
* #Override public String toString(LocalDate value) {
* return defaultConverter.toString(value);
* }
*
* #Override public LocalDate fromString(String text) {
* try {
* return defaultConverter.fromString(text);
* } catch (DateTimeParseException ex) {
* System.err.println("HelloDatePicker: "+ex.getMessage());
* throw ex;
* }
* }
* });
* </code></pre>
*
* #see javafx.scene.control.ComboBox#converterProperty
*/
public final ObjectProperty<StringConverter<LocalDate>> converterProperty() { return converter; }
private ObjectProperty<StringConverter<LocalDate>> converter =
new SimpleObjectProperty<StringConverter<LocalDate>>(this, "converter", null);
public final void setConverter(StringConverter<LocalDate> value) { converterProperty().set(value); }
public final StringConverter<LocalDate> getConverter() {
StringConverter<LocalDate> converter = converterProperty().get();
if (converter != null) {
return converter;
} else {
return defaultConverter;
}
}
private StringConverter<LocalDate> defaultConverter = new StringConverter<LocalDate>() {
#Override public String toString(LocalDate value) {
if (value != null) {
Locale locale = Locale.getDefault(Locale.Category.FORMAT);
Chronology chrono = getChronology();
ChronoLocalDate cDate;
try {
cDate = chrono.date(value);
} catch (DateTimeException ex) {
System.err.println(ex);
chrono = IsoChronology.INSTANCE;
cDate = value;
}
DateTimeFormatter dateFormatter =
DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT)
.withLocale(locale)
.withChronology(chrono)
.withDecimalStyle(DecimalStyle.of(locale));
String pattern =
DateTimeFormatterBuilder.getLocalizedDateTimePattern(FormatStyle.SHORT,
null, chrono, locale);
if (pattern.contains("yy") && !pattern.contains("yyy")) {
// Modify pattern to show four-digit year, including leading zeros.
String newPattern = pattern.replace("yy", "yyyy");
//System.err.println("Fixing pattern ("+forParsing+"): "+pattern+" -> "+newPattern);
dateFormatter = DateTimeFormatter.ofPattern(newPattern)
.withDecimalStyle(DecimalStyle.of(locale));
}
return dateFormatter.format(cDate);
} else {
return "";
}
}
#Override public LocalDate fromString(String text) {
if (text != null && !text.isEmpty()) {
Locale locale = Locale.getDefault(Locale.Category.FORMAT);
Chronology chrono = getChronology();
String pattern =
DateTimeFormatterBuilder.getLocalizedDateTimePattern(FormatStyle.SHORT,
null, chrono, locale);
DateTimeFormatter df =
new DateTimeFormatterBuilder().parseLenient()
.appendPattern(pattern)
.toFormatter()
.withChronology(chrono)
.withDecimalStyle(DecimalStyle.of(locale));
TemporalAccessor temporal = df.parse(text);
ChronoLocalDate cDate = chrono.date(temporal);
return LocalDate.from(cDate);
}
return null;
}
};
// --- Editor
/**
* The editor for the DatePicker.
*
* #see javafx.scene.control.ComboBox#editorProperty
*/
private ReadOnlyObjectWrapper<TextField> editor;
public final TextField getEditor() {
return editorProperty().get();
}
public final ReadOnlyObjectProperty<TextField> editorProperty() {
if (editor == null) {
editor = new ReadOnlyObjectWrapper<TextField>(this, "editor");
editor.set(new ComboBoxListViewSkin.FakeFocusTextField());
}
return editor.getReadOnlyProperty();
}
/** {#inheritDoc} */
#Override protected Skin<?> createDefaultSkin() {
return new DatePickerSkin(this);
}
/***************************************************************************
* *
* Stylesheet Handling *
* *
**************************************************************************/
private static final String DEFAULT_STYLE_CLASS = "date-picker";
/**
* #treatAsPrivate implementation detail
*/
private static class StyleableProperties {
private static final String country =
Locale.getDefault(Locale.Category.FORMAT).getCountry();
private static final CssMetaData<DatePicker, Boolean> SHOW_WEEK_NUMBERS =
new CssMetaData<DatePicker, Boolean>("-fx-show-week-numbers",
BooleanConverter.getInstance(),
(!country.isEmpty() &&
ControlResources.getNonTranslatableString("DatePicker.showWeekNumbers").contains(country))) {
#Override public boolean isSettable(DatePicker n) {
return n.showWeekNumbers == null || !n.showWeekNumbers.isBound();
}
#Override public StyleableProperty<Boolean> getStyleableProperty(DatePicker n) {
return (StyleableProperty)n.showWeekNumbersProperty();
}
};
private static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES;
static {
final List<CssMetaData<? extends Styleable, ?>> styleables =
new ArrayList<CssMetaData<? extends Styleable, ?>>(Control.getClassCssMetaData());
Collections.addAll(styleables,
SHOW_WEEK_NUMBERS
);
STYLEABLES = Collections.unmodifiableList(styleables);
}
}
/**
* #return The CssMetaData associated with this class, which may include the
* CssMetaData of its super classes.
*/
public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() {
return StyleableProperties.STYLEABLES;
}
/**
* {#inheritDoc}
*/
#Override
public List<CssMetaData<? extends Styleable, ?>> getControlCssMetaData() {
return getClassCssMetaData();
}
}
...and thanks in advance for being patient and helping me out.
The best way to change appearance like this is to use an external CSS. You can refer to the source code for the default stylesheet to see how the defaults are defined: the interesting parts for the DatePicker are near the bottom (lines 2932 onwards at the time of writing).
You can change the font size for the entire control just by doing
.date-picker {
-fx-font-size: 18pt ;
}
That ends up looking a little cluttered, so adding a bit of padding around the day names helps. The arrows will automatically scale to fit, so they will increase size with the increased font size on the month and year labels.
Here's a complete example:
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Date;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.DatePicker;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class DatePickerExample extends Application {
#Override
public void start(Stage primaryStage) {
DatePicker datePicker = new DatePicker();
VBox root = new VBox(datePicker);
Scene scene = new Scene(root, 250, 150);
scene.getStylesheets().add("date-picker-readable.css");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
date-picker-readable.css:
.date-picker {
-fx-font-size: 18pt;
-fx-font-weight: bold ;
}
.date-picker .day-name-cell {
-fx-padding: 10px ;
}
I tried to save data to json string in a txt file using Gson and then restore it using Gson either. Things go well if I do it in eclipse. But when packaged to jar, Gson throws Exceptions.
Here is the code for saving the file.
String gsonStr = gson.toJson(masterShips); // masterShips is ArrayList<Ship>
BufferedWriter writer = null;
try {
writer = new BufferedWriter(new FileWriter("D:\\master_ship.txt"));
writer.write(gsonStr);
} catch (IOException e) {
System.err.println(e);
} finally {
if (writer != null) {
try {
writer.close();
} catch (IOException e) {
System.err.println(e);
}
}
}
Then I read the file in eclipse using this code (and it works):
Scanner in = new Scanner(new FileReader("D:\\master_ship.txt"));
String str = in.nextLine();
Log.toDebug(str);
in.close();
JsonParser parser = new JsonParser();
JsonElement je = parser.parse(str);
JsonArray ja = je.getAsJsonArray();
for (int i=0; i<ja.size(); ++i) {
...
}
But after packaged into jar and run in cmd, Exception occurs:
Exception in thread "main" com.google.gson.JsonSyntaxException: com.google.gson.
stream.MalformedJsonException: Use JsonReader.setLenient(true) to accept malform
ed JSON at line 1 column 4
at com.google.gson.JsonParser.parse(JsonParser.java:65)
at com.google.gson.JsonParser.parse(JsonParser.java:45)
at kan.util.Master.loadMasterShip(Master.java:44)
at kan.util.Master.load(Master.java:27)
at kan.Main.main(Main.java:22)
Caused by: com.google.gson.stream.MalformedJsonException: Use JsonReader.setLeni
ent(true) to accept malformed JSON at line 1 column 4
at com.google.gson.stream.JsonReader.syntaxError(JsonReader.java:1505)
at com.google.gson.stream.JsonReader.checkLenient(JsonReader.java:1386)
at com.google.gson.stream.JsonReader.doPeek(JsonReader.java:531)
at com.google.gson.stream.JsonReader.peek(JsonReader.java:414)
at com.google.gson.JsonParser.parse(JsonParser.java:60)
... 4 more
According to the hint of the Exception, I changed my code and it still works in eclipse:
Scanner in = new Scanner(new FileReader("D:\\master_ship.txt"));
String str = in.nextLine();
in.close();
Reader reader = new StringReader(str);
JsonReader jr = new JsonReader(reader);
jr.setLenient(true);
JsonParser parser = new JsonParser();
JsonElement je = parser.parse(jr);
JsonArray ja = je.getAsJsonArray();
for (int i=0; i<ja.size(); ++i) {
...
}
But jar failed and throws
Exception in thread "main" java.lang.IllegalStateException: This is not a JSON A
rray.
at com.google.gson.JsonElement.getAsJsonArray(JsonElement.java:106)
at kan.util.Master.loadMasterShip(Master.java:58)
at kan.util.Master.load(Master.java:30)
at kan.Main.main(Main.java:22)
As suggested by Sotirios I cut the length of the arraylist down, and when I increase the number of ships to 4, things go wrong. Here is the json:
[{"id":1,"name":"睦月","type":2,"rank":2,"fuelMax":15,"bulletMax":15,"slotNum":2,"speed":10,"afterLv":20,"afterId":254,"range":1,"powerups":[1,1,0,0]},{"id":2,"name":"如月","type":2,"rank":1,"fuelMax":15,"bulletMax":15,"slotNum":2,"speed":10,"afterLv":20,"afterId":255,"range":1,"powerups":[0,1,0,0]},{"id":6,"name":"長月","type":2,"rank":1,"fuelMax":15,"bulletMax":15,"slotNum":2,"speed":10,"afterLv":20,"afterId":258,"range":1,"powerups":[0,1,0,0]},{"id":7,"name":"三日月","type":2,"rank":1,"fuelMax":15,"bulletMax":15,"slotNum":2,"speed":10,"afterLv":20,"afterId":260,"range":1,"powerups":[0,1,0,0]}]
↑ colunm 473
Exception in thread "main" com.google.gson.JsonSyntaxException: com.google.gson.
stream.MalformedJsonException: Unterminated object at line 1 column 473
at com.google.gson.internal.Streams.parse(Streams.java:56)
at com.google.gson.JsonParser.parse(JsonParser.java:84)
at kan.util.Master.loadMasterShip(Master.java:55)
at kan.util.Master.load(Master.java:30)
at kan.Main.main(Main.java:22)
Caused by: com.google.gson.stream.MalformedJsonException: Unterminated object at
line 1 column 473
at com.google.gson.stream.JsonReader.syntaxError(JsonReader.java:1505)
at com.google.gson.stream.JsonReader.doPeek(JsonReader.java:480)
at com.google.gson.stream.JsonReader.hasNext(JsonReader.java:403)
at com.google.gson.internal.bind.TypeAdapters$25.read(TypeAdapters.java:
666)
at com.google.gson.internal.bind.TypeAdapters$25.read(TypeAdapters.java:
659)
at com.google.gson.internal.bind.TypeAdapters$25.read(TypeAdapters.java:
642)
at com.google.gson.internal.Streams.parse(Streams.java:44)
... 4 more
Can anyone help me with this, you will be reaally preciated!
Use this class
import java.util.List;
public class GsonResponse
{
public int id;
public String name;
public int type;
public int rank;
public int fuelMax;
public int bulletMax;
public int slotNum;
public int speed;
public int afterLv;
public int afterId;
public int range;
public List<Integer> powerups;
/**
* #return the id
*/
public int getId() {
return id;
}
/**
* #param id the id to set
*/
public void setId(int id) {
this.id = id;
}
/**
* #return the name
*/
public String getName() {
return name;
}
/**
* #param name the name to set
*/
public void setName(String name) {
this.name = name;
}
/**
* #return the type
*/
public int getType() {
return type;
}
/**
* #param type the type to set
*/
public void setType(int type) {
this.type = type;
}
/**
* #return the rank
*/
public int getRank() {
return rank;
}
/**
* #param rank the rank to set
*/
public void setRank(int rank) {
this.rank = rank;
}
/**
* #return the fuelMax
*/
public int getFuelMax() {
return fuelMax;
}
/**
* #param fuelMax the fuelMax to set
*/
public void setFuelMax(int fuelMax) {
this.fuelMax = fuelMax;
}
/**
* #return the bulletMax
*/
public int getBulletMax() {
return bulletMax;
}
/**
* #param bulletMax the bulletMax to set
*/
public void setBulletMax(int bulletMax) {
this.bulletMax = bulletMax;
}
/**
* #return the slotNum
*/
public int getSlotNum() {
return slotNum;
}
/**
* #param slotNum the slotNum to set
*/
public void setSlotNum(int slotNum) {
this.slotNum = slotNum;
}
/**
* #return the speed
*/
public int getSpeed() {
return speed;
}
/**
* #param speed the speed to set
*/
public void setSpeed(int speed) {
this.speed = speed;
}
/**
* #return the afterLv
*/
public int getAfterLv() {
return afterLv;
}
/**
* #param afterLv the afterLv to set
*/
public void setAfterLv(int afterLv) {
this.afterLv = afterLv;
}
/**
* #return the afterId
*/
public int getAfterId() {
return afterId;
}
/**
* #param afterId the afterId to set
*/
public void setAfterId(int afterId) {
this.afterId = afterId;
}
/**
* #return the range
*/
public int getRange() {
return range;
}
/**
* #param range the range to set
*/
public void setRange(int range) {
this.range = range;
}
/**
* #return the powerups
*/
public List<Integer> getPowerups() {
return powerups;
}
/**
* #param powerups the powerups to set
*/
public void setPowerups(List<Integer> powerups) {
this.powerups = powerups;
}
}
just add below code where u parse
String strJson = "[{\"id\":1,\"name\":\"睦月\",\"type\":2,\"rank\":2,\"fuelMax\":15,\"bulletMax\":15,\"slotNum\":2,\"speed\":10,\"afterLv\":20,\"afterId\":254,\"range\":1,\"powerups\":[1,1,0,0]},{\"id\":2,\"name\":\"如月\",\"type\":2,\"rank\":1,\"fuelMax\":15,\"bulletMax\":15,\"slotNum\":2,\"speed\":10,\"afterLv\":20,\"afterId\":255,\"range\":1,\"powerups\":[0,1,0,0]},{\"id\":6,\"name\":\"長月\",\"type\":2,\"rank\":1,\"fuelMax\":15,\"bulletMax\":15,\"slotNum\":2,\"speed\":10,\"afterLv\":20,\"afterId\":258,\"range\":1,\"powerups\":[0,1,0,0]},{\"id\":7,\"name\":\"三日月\",\"type\":2,\"rank\":1,\"fuelMax\":15,\"bulletMax\":15,\"slotNum\":2,\"speed\":10,\"afterLv\":20,\"afterId\":260,\"range\":1,\"powerups\":[0,1,0,0]}]";
GsonResponse gsonResponse = null;
try {
gsonResponse = new Gson().fromJson(strJson,
GsonResponse.class);
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}