Я придумал другой подход, который мог бы преобразовать любой struct
без хлопот фиксирующей длины, однако полученный массив байтов имел бы немного больше накладных расходов.
Вот пример struct
:
[StructLayout(LayoutKind.Sequential)]
public class HelloWorld
{
public MyEnum enumvalue;
public string reqtimestamp;
public string resptimestamp;
public string message;
public byte[] rawresp;
}
Как вы можете видеть, для всех этих структур потребуется добавить атрибуты фиксированной длины. Который часто мог занять больше места, чем требовалось. Обратите внимание, что требуется LayoutKind.Sequential
, так как мы хотим, чтобы отражение всегда давало нам тот же порядок, когда тянул за FieldInfo
. Мое вдохновение - от TLV
Type-Length-Value. Давайте посмотрим на код:
public static byte[] StructToByteArray<T>(T obj)
{
using (MemoryStream ms = new MemoryStream())
{
FieldInfo[] infos = typeof(T).GetFields(BindingFlags.Public | BindingFlags.Instance);
foreach (FieldInfo info in infos)
{
BinaryFormatter bf = new BinaryFormatter();
using (MemoryStream inms = new MemoryStream()) {
bf.Serialize(inms, info.GetValue(obj));
byte[] ba = inms.ToArray();
// for length
ms.Write(BitConverter.GetBytes(ba.Length), 0, sizeof(int));
// for value
ms.Write(ba, 0, ba.Length);
}
}
return ms.ToArray();
}
}
Вышеупомянутая функция просто использует BinaryFormatter
для сериализации неизвестного размера raw object
, и я просто отслеживаю размер и сохраняю его внутри выхода MemoryStream
тоже.
public static void ByteArrayToStruct<T>(byte[] data, out T output)
{
output = (T) Activator.CreateInstance(typeof(T), null);
using (MemoryStream ms = new MemoryStream(data))
{
byte[] ba = null;
FieldInfo[] infos = typeof(T).GetFields(BindingFlags.Public | BindingFlags.Instance);
foreach (FieldInfo info in infos)
{
// for length
ba = new byte[sizeof(int)];
ms.Read(ba, 0, sizeof(int));
// for value
int sz = BitConverter.ToInt32(ba, 0);
ba = new byte[sz];
ms.Read(ba, 0, sz);
BinaryFormatter bf = new BinaryFormatter();
using (MemoryStream inms = new MemoryStream(ba))
{
info.SetValue(output, bf.Deserialize(inms));
}
}
}
}
Когда мы хотим преобразовать его обратно в исходное struct
, мы просто прочтем длину и вернем его обратно в BinaryFormatter
, который в свою очередь сбрасывает его обратно в struct
.
Эти 2 функции являются общими и должны работать с любым struct
, я протестировал вышеуказанный код в моем проекте C#
, где у меня есть сервер и клиент, подключенный и обменивающийся через NamedPipeStream
и я пересылаю свой struct
в виде массива байтов от одного и к другому и преобразовываю его обратно.
Я считаю, что мой подход может быть лучше, поскольку он не фиксирует длину самого struct
и единственные накладные расходы - это всего лишь int
для каждого поля, которое у вас есть в вашей структуре. Внутри байтового массива, генерируемого BinaryFormatter
, есть немного незначительных накладных расходов, но кроме этого, это немного.
Я выяснил, почему enctype="multipart/form-data"
не работает с jsp:setProperty and jsp:getProperty
. Поскольку я работаю с tomcat, process.jsp был сгенерирован как process_jsp.java.
dao.User bean = null;
bean = (dao.User) _jspx_page_context.getAttribute("bean", javax.servlet.jsp.PageContext.PAGE_SCOPE);
if (bean == null){
bean = new dao.User();
_jspx_page_context.setAttribute("bean", bean, javax.servlet.jsp.PageContext.PAGE_SCOPE);
out.write('\n');
org.apache.jasper.runtime.JspRuntimeLibrary.introspect(_jspx_page_context.findAttribute("bean"), request);
out.write(' ');
out.write(' ');
out.write('\n');
}
В соответствии с вышеуказанным исходным кодом org.apache.jasper.runtime.JspRuntimeLibrary.introspect
был назван.
public static void introspect(Object bean, ServletRequest request) throws JasperException
{
Enumeration<String> e = request.getParameterNames();
while ( e.hasMoreElements() ) {
String name = e.nextElement();
String value = request.getParameter(name);
introspecthelper(bean, name, value, request, name, true);
}
}
Над параметрами и именами запроса карты кода (именами свойств бинов) и затем introspecthelper
будет передано значение в соответствующие методы установки с помощью java.lang.reflect.Method.invoke
.
При работе с enctype="multipart/form-data"
проблема заключается в Enumeration<String> e = request.getParameterNames()
. Элемент не был найден, поэтому introspecthelper
никогда не выполняется.
Потому что JspRuntimeLibrary.introspect
является статическим методом. Я не могу отменить его поведение. Итак, написание пользовательского тега или следование Как загрузить файлы на сервер с помощью JSP / Servlet? - единственный способ решить эту проблему.