1 /* 2 * Copyright 2002-2018 the original author or authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 module hunt.stomp.converter.JsonMessageConverter; 18 19 import hunt.stomp.converter.AbstractMessageConverter; 20 import hunt.stomp.Message; 21 import hunt.stomp.MessageHeaders; 22 import hunt.stomp.MessagingException; 23 import hunt.stomp.support.GenericMessage; 24 import hunt.stomp.support.MessageBuilder; 25 import hunt.stomp.support.MessageHeaderAccessor; 26 27 import hunt.util.MimeType; 28 import hunt.text.Charset; 29 import hunt.Exceptions; 30 import hunt.Nullable; 31 import hunt.logging; 32 import hunt.util.TypeUtils; 33 34 import std.array; 35 import std.conv; 36 import std.json; 37 import std.string; 38 39 /** 40 */ 41 class JsonMessageConverter : AbstractMessageConverter { 42 43 private bool prettyPrint; 44 45 /** 46 * Construct a {@code JsonMessageConverter} supporting 47 * the {@code application/json} MIME type with {@code UTF-8} character set. 48 */ 49 this() { 50 super(new MimeType("application/json", StandardCharsets.UTF_8)); 51 } 52 53 /** 54 * Construct a {@code JsonMessageConverter} supporting 55 * one or more custom MIME types. 56 * @param supportedMimeTypes the supported MIME types 57 * @since 4.1.5 58 */ 59 this(MimeType[] supportedMimeTypes...) { 60 super(supportedMimeTypes.dup); 61 } 62 63 /** 64 * Whether to use the {@link DefaultPrettyPrinter} when writing JSON. 65 * This is a shortcut for setting up an {@code ObjectMapper} as follows: 66 * <pre class="code"> 67 * ObjectMapper mapper = new ObjectMapper(); 68 * mapper.configure(SerializationFeature.INDENT_OUTPUT, true); 69 * converter.setObjectMapper(mapper); 70 * </pre> 71 */ 72 void setPrettyPrint(bool prettyPrint) { 73 this.prettyPrint = prettyPrint; 74 } 75 76 77 override 78 protected bool canConvertFrom(MessageBase message, TypeInfo targetClass) { 79 bool r = true; 80 81 if (targetClass is null || !supportsMimeType(message.getHeaders())) { 82 r = false; 83 } 84 85 version(HUNT_DEBUG) { 86 if(targetClass is null) 87 tracef("checking message, converter: %s, result: %s", 88 TypeUtils.getSimpleName(typeid(this)), r); 89 else 90 tracef("checking message, target: %s, converter: %s, result: %s", targetClass, 91 TypeUtils.getSimpleName(typeid(this)), r); 92 } 93 94 return r; 95 } 96 97 override 98 protected bool canConvertTo(Object payload, MessageHeaders headers, TypeInfo conversionHint) { 99 bool r = supportsMimeType(headers); 100 version(HUNT_DEBUG) tracef("checking payload, type: %s, converter: %s, result: %s", 101 typeid(payload), TypeUtils.getSimpleName(typeid(this)), r); 102 103 return r; 104 } 105 106 override 107 protected bool supports(TypeInfo typeInfo) { 108 // should not be called, since we override canConvertFrom/canConvertTo instead 109 throw new UnsupportedOperationException(); 110 } 111 112 override 113 protected Object convertFromInternal(MessageBase message, TypeInfo targetClass, TypeInfo conversionHint) { 114 auto m = cast(GenericMessage!(byte[]))message; 115 if(m is null) { 116 warningf("Wrong message type: %s", conversionHint); 117 return null; 118 } 119 string payload = cast(string)m.getPayload(); 120 try { 121 JSONValue jv = parseJSON(payload); 122 auto r = new Nullable!(JSONValue)(jv); 123 return r; 124 } catch(Exception ex) { 125 errorf("json convertion error: %s", ex.msg); 126 infof("message payload: %s", payload); 127 throw new MessageConversionException(message, "Could not read JSON: " ~ ex.msg, ex); 128 } 129 } 130 131 override 132 protected Object convertToInternal(Object payload, MessageHeaders headers, 133 TypeInfo conversionHint) { 134 135 auto p = cast(Nullable!(JSONValue))payload; 136 if(p is null) { 137 warningf("Wrong message type: %s", conversionHint); 138 return null; 139 } 140 141 JSONValue jv = p.value(); 142 try { 143 string r; 144 if(this.prettyPrint) 145 r = jv.toPrettyString(); 146 else 147 r = jv.toString(); 148 return new Nullable!string(r); 149 } catch(Exception ex) { 150 warning(ex.msg); 151 throw new MessageConversionException("Could not write JSON: " ~ ex.msg, ex); 152 } 153 } 154 }